This question already has answers here:
How to deserialize JSON with duplicate property names in the same object
(3 answers)
Closed 6 years ago.
I'm trying to parse a JSON string to a JObject, but somehow it only parses the first object of an Array.
This is a part of a JSON string
{
"Categories": [
{
"Category": [
{
"ID": "1",
"Description": "Kochen/Backen",
"IsActive": "True"
}
],
"Category":[
{
"ID": "2",
"Description": "Sport",
"IsActive": "True"
}
],
"Category": [
{
"ID": "3",
"Description": "Begleitung 2",
"IsActive": "True"
}
]
}
],
And after JObject.Parse, I can see it loaded:
{
"Categories": [
{
"Category": [
{
"ID": "3",
"Description": "Begleitung 2",
"IsActive": "True"
}
]
}
],
So why are the first 2 Categories not parsed?
I'm not a pro with JSON, so I don't know if the string is correct that way.
Thanks for your help
Your JObject cannot hold duplicate keys. The dictionary in the parent list, has multiple entries with same key Category:
{
"Category": [
{
"ID": "1",
"Description": "Kochen/Backen",
"IsActive": "True"
}
],
"Category":[
{
"ID": "2",
"Description": "Sport",
"IsActive": "True"
}
],
"Category": [
{
"ID": "3",
"Description": "Begleitung 2",
"IsActive": "True"
}
]
}
So the other keys are overwritten after parsing and the last item with id 3 becomes the final value. Consider restructuring the keys into say Category1, Category2 and Category3
Related
In the Json string given below, I want to find all elements in which category = m AND the "middle" array contains elements which match this condition - the element's "middle" array has objects whose itemType = Executable.
I would like to use jsonpath to get the desired objects. I prefer to not use jmespath because it can be too complex for my purpose. But, I am new to jsonpath and I am not able to figure out the json query from online tutorials which are too trivial or basic. I wonder if its better to use a programming language instead to get the data I need. Please advise.
So far, I was able to only extract elements in which category = m by using this jsonpath query $.[?(#.category=="m")]. How do I do the remaining part ?
Json :
Overview - Every object has a "content" object. Each content object generally has a start, middle and end array besides other fields. Middle arrays can have multiple content objects inside them and so on. Some of the content objects have only a middle array. I am interested in locating items in such content objects as mentioned above.
Note that this is not the actual json which I have to process. It is an imitation which has been sanitized for SO.
{
"id": "123",
"contents": {
"title": "B1",
"start": [],
"middle": [
{
"level": "1",
"contents": {
"title": "C1",
"category": "c",
"start": [],
"middle": [
{
"level": "2",
"contents": {
"title": "M1",
"category": "m",
"start": [],
"middle": [
{
"level": "3",
"contents": {
"title": "MAT1",
"middle": [
{
"itemType": "Data"
}
]
}
},
{
"level": "3",
"contents": {
"title": "MAT2",
"middle": [
{
"itemType": "Executable",
"id": "exec1"
}
]
}
},
{
"level": "3",
"contents": {
"title": "MAT3",
"middle": [
{
"itemType": "Data"
}
]
}
}
],
"end": []
}
},
{
"level": "2",
"contents": {
"title": "M2",
"category": "m",
"start": [],
"middle": [
{
"level": "3",
"contents": {
"title": "MAT1",
"middle": [
{
"itemType": "Data"
}
]
}
},
{
"level": "3",
"contents": {
"title": "MAT2",
"middle": [
{
"itemType": "Executable",
"id": "exec2"
}
]
}
}
],
"end": []
}
}
],
"end": []
}
},
{
"level": "1",
"contents": {
"title": "C2",
"category": "c",
"start": [],
"middle": [
{
"level": "2",
"contents": {
"title": "M1",
"category": "m",
"start": [],
"middle": [
{
"level": "3",
"contents": {
"title": "MAT1",
"middle": [
{
"itemType": "Data"
}
]
}
},
{
"level": "3",
"contents": {
"title": "MAT2",
"middle": [
{
"itemType": "Executable",
"id": "exec3"
}
]
}
},
{
"level": "3",
"contents": {
"title": "MAT3",
"middle": [
{
"itemType": "Data"
}
]
}
}
],
"end": []
}
},
{
"level": "2",
"contents": {
"title": "M2",
"category": "m",
"start": [],
"middle": [
{
"level": "3",
"contents": {
"title": "MAT1",
"middle": [
{
"itemType": "Data"
}
]
}
},
{
"level": "3",
"contents": {
"title": "MAT2",
"middle": [
{
"itemType": "Executable",
"id": "exec4"
}
]
}
},
{
"level": "3",
"contents": {
"title": "MAT3",
"middle": [
{
"itemType": "Data"
}
]
}
}
],
"end": []
}
}
],
"end": []
}
}
],
"end": []
}
}
Context
json with nested objects1
jsonpath expression language
choosing between jsonpath and jmespath (or other JSON expression engine)
Problem
DeveMasterJoe2 wants to extract some values from nested JSON
Discussion
There are lots of implementations of jsonpath out there, and they do not all support the same features
The structure and normalization of the source JSON is going to influence how easily this can be done with pure jsonpath
In choosing a JSON expression engine, one has to weigh multiple factors
how consistent are the implementations across languages?
how many choices are there within a given language?
how clear is the specification?
how many examples, unit-tests or tutorials are available?
who is supporting it?
Example solution using Python and jsonpath-ng
Here is an example solution using python 3.7 and jsonpath-ng
This example uses a mix of jsonpath and python instead of just pure jsonpath, because of the heavily-nested JSON
I will leave it for someone else to provide an answer that relies on pure jsonpath
Note that the source JSON arguably could stand to be cleaned up a bit
(for example, why is there no id field attached to itemType==Data elements?)
(for example, why is category not found on all contents elements?)
(for example, if you expressly specify level why complicate things with heavily nested objects when you can determine depth by level ?)
This example:
## import libraries
import codecs
import json
import jsonpath_ng
from jsonpath_ng.ext import parse
##;;
## init vars
href="path/to/my/jsonfile/nested_dict.json"
json_string = codecs.open(href, 'rb', encoding='utf8').read()
json_dataroot = json.loads(json_string)
final_result = []
##;;
## init jsonpath outer-query
match = parse('$..contents.middle[*]').find(json_dataroot)
##;;
## iterate through outer-query and gather subelements
for ijj,item in enumerate(match):
## restrict to desired category == 'm'
if(match[ijj].value.get('contents',{}).get('category','') == 'm'):
## extract out desired subelements
json_datafrag001 = [item.get('contents',{}).get('middle',{})[0]
for item in match[ijj].value.get('contents',{}).get('middle',{})
]
match001 = parse("$[?(#.itemType=='Executable')]").find(json_datafrag001)
final_result.extend(list(match001[ikk].value for ikk,item in enumerate(match001)))
pass
##;;
## show final result
vout = json.dumps(final_result, sort_keys=True,indent=4, separators=(',', ': '))
print(vout)
##;;
... produces this result ...
[
{
"id": "exec1",
"itemType": "Executable"
},
{
"id": "exec2",
"itemType": "Executable"
},
{
"id": "exec3",
"itemType": "Executable"
},
{
"id": "exec4",
"itemType": "Executable"
}
]
1 (aka dictionary, associative-array, hash)
I have a an object. I am able to sort the items by using lodash's _.orderBy().
However, in one of the scenario I have to sort by subject, which is an array of objects. Items inside the subject array are already sorted based on the name.
As subject is an array of the objects, I need to consider the first item for sorting.
[
{
"id": "1",
"name": "peter",
"subject": [
{
"id": "1",
"name": "maths"
},
{
"id": "2",
"name": "social"
}
]
},
{
"id": "2",
"name": "david",
"subject": [
{
"id": "2",
"name": "physics"
},
{
"id": "3",
"name": "science"
}
]
},
{
"id": "3",
"name": "Justin",
"subject": [
]
}
]
You can use _.get() to extract the name (or id) of the 1st item in subjects. If no item exists, _.get() will return undefined, which can be replaced with a default value. In this case, we don't want to use an empty string as a default value, since the order would change. Instead I'm checking if the value is a string, if it is I use lower case on it, if not I return it as is.
const arr = [{"id":"1","name":"peter","subject":[{"id":"1","name":"maths"},{"id":"2","name":"social"}]},{"id":"2","name":"david","subject":[{"id":"2","name":"physics"},{"id":"3","name":"science"}]},{"id":"3","name":"Justin","subject":[]}]
const result = _.orderBy(arr, o => {
const name = _.get(o, 'subject[0].name')
return _.isString(name) ? name.toLowerCase() : name
})
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
Use _.sortBy with a comparison/sorting function argument. Your function itself can look into the receiving arguments subject key (I think its the subject you want to compare?)
Since you have the question also tagged with ES6 here is an JS only solution via Array.sort:
let arr = [ { "id": "1", "name": "peter", "subject": [ { "id": "1", "name": "maths" }, { "id": "2", "name": "social" } ] }, { "id": "2", "name": "david", "subject": [ { "id": "2", "name": "physics" }, { "id": "3", "name": "science" } ] }, { "id": "3", "name": "Justin", "subject": [] }, ]
const result = arr.sort((a,b) =>
a.subject.length && b.subject.length
? a.subject[0].name.localeCompare(b.subject[0].name)
: a.subject.length ? -1 : 1)
console.log(result)
I am trying to utilize the JSON result of a GET request to my Li3 app, but I would like the result to be an array of the returned JSON objects, rather than an object of the JSON objects.
I have the following code in my view file (index.html.php):
print($todos->to('json'));
Which results in each row becoming a JSON object (good), but within an over-arching JSON object.
{
"1": {
"id": "1",
"title": "One",
"done": "0"
},
"2": {
"id": "2",
"title": "Two",
"done": "0"
},
"3": {
"id": "3",
"title": "Three",
"done": "0"
},
"4": {
"id": "4",
"title": "Four",
"done": "0"
}
}
I would like to get:
[
{
"id": "1",
"title": "One",
"done": "0"
},
{
"id": "2",
"title": "Two",
"done": "0"
},
{
"id": "3",
"title": "Three",
"done": "0"
},
{
"id": "4",
"title": "Four",
"done": "0"
}
]
Note: I've found that this was the case (array of objects) in commit "974469cf25db5cbab61f3e1ff172405f4635032e" of the lithium github project, but with anything after that commit, the result is an object of objects.
Try $todos->to('json', ['indexed' => false]), or, refer to the Media class for direct serialization of JSON without the template.
Todos::all(['return' => 'array'))->to('json'); works perfect with RecordSet too
I want to index & search nested json in solr. Here is my json code
{
"id": "44444",
"headline": "testing US",
"generaltags": [
{
"type": "person",
"name": "Jayalalitha",
"relevance": "0.334",
"count": 1
},
{
"type": "person",
"name": "Kumar",
"relevance": "0.234",
"count": 1
}
],
"socialtags": {
"type": "SocialTag",
"name": "US",
"importance": 2
},
"topic": {
"type": "Topic",
"name": "US",
"score": "0.936"
}
}
When I try to Index, I'm getting the error "Error parsing JSON field value. Unexpected OBJECT_START"
When we tried to use Multivalued Field & index, we couldn't able to search using the multivalued field? Its returning "Undefined Field"
Also Please advice if I need to do any changes in schema.xml file?
You are nesting child documents within your document. You need to use the proper syntax for nested child documents in JSON:
[
{
"id": "1",
"title": "Solr adds block join support",
"content_type": "parentDocument",
"_childDocuments_": [
{
"id": "2",
"comments": "SolrCloud supports it too!"
}
]
},
{
"id": "3",
"title": "Lucene and Solr 4.5 is out",
"content_type": "parentDocument",
"_childDocuments_": [
{
"id": "4",
"comments": "Lots of new features"
}
]
}
]
Have a look at this article which describes JSON child documents and block joins.
Using the format mentioned by #qux you will face "Expected: OBJECT_START but got ARRAY_START at [16]",
"code": 400
as when JSON starting with [....] will parsed as a JSON array
{
"id": "44444",
"headline": "testing US",
"generaltags": [
{
"type": "person",
"name": "Jayalalitha",
"relevance": "0.334",
"count": 1
},
{
"type": "person",
"name": "Kumar",
"relevance": "0.234",
"count": 1
}
],
"socialtags": {
"type": "SocialTag",
"name": "US",
"importance": 2
},
"topic": {
"type": "Topic",
"name": "US",
"score": "0.936"
}
}
The above format is correct.
Regarding searching. Kindly use the index to search for the elements of the JSON array.
The workaround for this can be keeping the whole JSON object inside other JSON object and the indexing it
I was suggesting to keep the whole data inside another JSON object. You can try the following way
{
"data": [
{
"id": "44444",
"headline": "testing US",
"generaltags": [
{
"type": "person",
"name": "Jayalalitha",
"relevance": "0.334",
"count": 1
},
{
"type": "person",
"name": "Kumar",
"relevance": "0.234",
"count": 1
}
],
"socialtags": {
"type": "SocialTag",
"name": "US",
"importance": 2
},
"topic": {
"type": "Topic",
"name": "US",
"score": "0.936"
}
}
]
}
see the syntax in http://yonik.com/solr-nested-objects/
$ curl http://localhost:8983/solr/demo/update?commitWithin=3000 -d '
[
{id : book1, type_s:book, title_t : "The Way of Kings", author_s : "Brandon Sanderson",
cat_s:fantasy, pubyear_i:2010, publisher_s:Tor,
_childDocuments_ : [
{ id: book1_c1, type_s:review, review_dt:"2015-01-03T14:30:00Z",
stars_i:5, author_s:yonik,
comment_t:"A great start to what looks like an epic series!"
}
,
{ id: book1_c2, type_s:review, review_dt:"2014-03-15T12:00:00Z",
stars_i:3, author_s:dan,
comment_t:"This book was too long."
}
]
}
]'
supported from solr 5.3
I am using JSON to dynamically populate a parent/child dropdown lists. I have it working to fill the parent drop down and have the child drop down filling with the same parent data. How do I loop through the sub array of the selected value from the parent?
My "each" line (key is the value from my parent DDL:
var key = $(this).prev("input").val();
$.each(jsondata.Id[key].HourTypeCodes, function(i, item) {
My JSON
[{
"Id": 1,
"Title": "Vacation",
"HourTypeCodes": [
{
"Id": "05",
"Title": "VAC POLICE/FIRE"
},
{
"Id": "04",
"Title": "VACATION"
},
{
"Id": "62",
"Title": "VACATION HOURS PURCHASED"
},
{
"Id": "60",
"Title": "VACATION SELL BACK"
}
]
},
{
"Id": 2,
"Title": "Holiday",
"HourTypeCodes": [
{
"Id": "08",
"Title": "HOLIDAY"
}
]
},
{
"Id": 3,
"Title": "Floating Holiday",
"HourTypeCodes": [
{
"Id": "09",
"Title": "FLOATING HOLIDAY"
}
]
}]
I figured it out finally! I found this: http://inderpreetsingh.com/2010/10/14/javascriptjson-find-index-in-an-array-of-objects/
From there these are the two lines that work:
var i = findIndexByKeyValue(jsonRequestType, "Id", key);
$.each(jsonRequestType[i].HourTypeCodes, function(i, item) {