JSON: Serialize a dictionary with complex object in key - json

What is the best way to serialize a dictionary when key is a complex type. For example consider this invalid json:
[
{ParentId:1, ParentName:'X'}:
[{'ChildId':'1', 'ChildName':'a'}, {'ChildId':'2', 'ChildName':'b'}],
{ParentId:2, ParentName:'Y'}:
[{'ChildId':"3", 'ChildName':'c'}]}
]
Is there any way to correctly serialize this?

You can wrap the json with keys as strings and values as your complex object.
In addition, your keys of the complex type should be strings as well.
For example:
[
{
"name": {
"ParentId": 1,
"ParentName": "X"
},
"value": [
{
"ChildId": "1",
"ChildName": "a"
},
{
"ChildId": "2",
"ChildName": "b"
}
]
},
{
"name": {
"ParentId": 2,
"ParentName": "Y"
},
"value": [
{
"ChildId": "3",
"ChildName": "c"
}
]
}
]

Unfortunately this is not a valid json, so the answer would be no :-( keys have to be strings. You could reorganize your data structure to wrap your children list into a "parent" object which has an id and a name !
Basically it would look like :
[
{
"ParentId": 1,
"ParentName": "X",
"children": [
{
"ChildId": 1,
"ChildName": "a"
},
{
"ChildId": 2,
"ChildName": "b"
}
]
},
{
"ParentId": 2,
"ParentName": "Y",
"children": [
{
"ChildId": 3,
"ChildName": "c"
}
]
}
]
You can find the specifications of JSON here : https://www.rfc-editor.org/rfc/rfc4627

Related

JsonPath - Extract object meeting multiple criteria?

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)

lodash sort an array of objects by a property which has an array of objects

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)

Traversing through nested json in scala play

I'm using scala play and am attempting to traverse a json tree in order to validate that specific name values have specific children with specific name values. I have the following Json in the form of a JsObject:
{ "name": "user", "children": [ { "name": "$a", "children": [ { "name": "foo", "children": [ ] }, { "name": "fooBar", "children": [ { "name": "$a", "children": [ { "name": "subFoobar1", "children": [ ] }, { "name": "subFoobar2", "children": [ { "name": "TEST", "children": [ ] } ] }, { "name": "subFoobar3", "children": [ ] } ] } ] }, { "name": "bar", "children": [ { "name": "$a", "children": [ ] }, { "name": "$c", "children": [ ] }, { "name": "$b", "children": [ ] } ] }, { "name": "barFoo", "children": [ ] } ] } ] }
Ideally I would use nested for loops to traverse but the JsObject structure is preventing me from accessing the underlying values when attempting traverse. I have also attempted mapping the JsObject to a map of type [Map[String,Map[String,Any]]] but I am getting invalid cast compiler errors.
Any tips on how I can traverse and validate the name value at each level would be appreciated. I would preferably like to use the play json library
Issue was in the case class I was attempting to use. I wasn't accounting for the recursive nature of my Json structure
case class ActorTree(name : String, children:Seq[ActorTree] )

JSON Schema for tree structure

I have to build tree like structure of Json data.Each node has an id (an integer, required), a label (a string, optional), and an array of child nodes (optional). Can you help me how to write JSON schema for this Json data. I need to set Id as required in child node as well.
{
"Id": 1,
"Label": "A",
"Child": [
{
"Id": 2,
"Label": "B",
"Child": [
{
"Id": 5,
"Label": "E"
}, {
"Id": 6,
"Label": "E"
}, {
"Id": 7,
"Label": "E"
}
]
}, {
"Id": 3,
"Label": "C"
}, {
"Id": 4,
"Label": "D",
"Child": [
{
"Id": 8,
"Label": "H"
}, {
"Id": 9,
"Label": "I"
}
]
}
]
}
A schema for this structure only needs a definition of a node and a reference to that node. The property Children (renamed from Child) references the node as well.
Here's the schema:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"$ref": "#/definitions/node",
"definitions": {
"node": {
"properties": {
"Id": {
"type": "integer"
},
"Label": {
"type": "string"
},
"Children": {
"type": "array",
"items": {
"$ref": "#/definitions/node"
}
}
},
"required": [
"Id"
]
}
}
}

Why is this JSON not valid?

JSONLint is giving me an error of "Expecting 'EOF'" at the comma that separates the "blah"s. I'm stumped.
{
"blah": false,
"heynow": [
{
"Name": "one",
"Duration": 2,
"DurationUnit": "Hours"
},
{
"Name": "two",
"Duration": 40,
"DurationUnit": "Minutes"
}
]
},
{
"blah": true,
"heynow": [
{
"Name": "three",
"Duration": 2,
"DurationUnit": "Hours"
},
{
"Name": "four",
"Duration": 40,
"DurationUnit": "Minutes"
}
]
}
The two JSON objects need to be wrapped in something. I suggest an array. For example:
[
{
"blah": false,
// ...
},
{
"blah": true,
//...
}
]
This looks like an array with two objects. In that case, the array is represented with enclosing [] in JSON.