How to extract key-value pair from nested json - json

I have as nested json object like this
{
"id": 1,
"parentId": null,
"name": "Product",
"children": [
{
"id": 50,
"parentId": 1,
"name": "Bicycle",
"children": [
{
"id": 100,
"parentId": 50,
"name": "Tire"
}
]
}
]
}
Oddly I have figured out how to build the nested tree from the result but not how to reverse it.
I have tried using lodash _.flatten and _.flattendeep but having one of those days where I can't get my head around this. Also the object can be of unknown depth. Any thoughts ?
My desired result is this.
[
{"id" : 1, "parentId" : null, "Product" },
{"id" : 50, "parentId" : 1 , "Bicycle"},
{"id" : 100, "parentId" : 50 , "Tire"}
]

not a very optimal solution but just an idea, NOTE: if dataset is very large there might be some performance issues.
resultsArray = [];
function flatten(obj) {
const { id, parentId, name, children} = obj;
if(children && children.length) {
children.map(child => flatten(child));
}
resultsArray.push({id, parentId, name});
}

Related

How to get the All index values in Groovy JSON xpath

Please find the attached Groovy code which I am using to get the particular filed from the response body.
Query 1 :
It is retrieving the results when the I am using the correct Index value like if the data.RenewalDetails[o], will give output as Value 1 and if the data.RenewalDetails[1], output as Value 2.
But in my real case, I will never know about number of blocks in the response, so I want to get all the values that are satisficing the condition, I tried data.RenewalDetails[*] but it is not working. Can you please help ?
Query 2:
Apart from the above condition, I want to add one more filter, where "FamilyCode": "PREMIUM" in the Itemdetails, Can you help on the same ?
def BoundId = new groovy.json.JsonSlurper().parseText('{"data":{"RenewalDetails":[{"ExpiryDetails":{"duration":"xxxxx","destination":"LHR","from":"AUH","value":2,"segments":[{"valudeid":"xxx-xx6262-xxxyyy-1111-11-11-1111"}]},"Itemdetails":[{"BoundId":"Value1","isexpired":true,"FamilyCode":"PREMIUM","availabilityDetails":[{"travelID":"AAA-AB1234-AAABBB-2022-11-10-1111","quota":"X","scale":"XXX","class":"X"}]}]},{"ExpiryDetails":{"duration":"xxxxx","destination":"LHR","from":"AUH","value":2,"segments":[{"valudeid":"xxx-xx6262-xxxyyy-1111-11-11-1111"}]},"Itemdetails":[{"BoundId":"Value2","isexpired":true,"FamilyCode":"PREMIUM","availabilityDetails":[{"travelID":"AAA-AB1234-AAABBB-2022-11-10-1111","quota":"X","scale":"XXX","class":"X"}]}]}]},"warnings":[{"code":"xxxx","detail":"xxxxxxxx","title":"xxxxxxxx"}]}')
.data.RenewalDetails[0].Itemdetails.find { itemDetail ->
itemDetail.availabilityDetails[0].travelID.length() == 33
}?.BoundId
println "Hello " + BoundId
Something like this:
def txt = '''\
{
"data": {
"RenewalDetails": [
{
"ExpiryDetails": {
"duration": "xxxxx",
"destination": "LHR",
"from": "AUH",
"value": 2,
"segments": [
{
"valudeid": "xxx-xx6262-xxxyyy-1111-11-11-1111"
}
]
},
"Itemdetails": [
{
"BoundId": "Value1",
"isexpired": true,
"FamilyCode": "PREMIUM",
"availabilityDetails": [
{
"travelID": "AAA-AB1234-AAABBB-2022-11-10-1111",
"quota": "X",
"scale": "XXX",
"class": "X"
}
]
}
]
},
{
"ExpiryDetails": {
"duration": "xxxxx",
"destination": "LHR",
"from": "AUH",
"value": 2,
"segments": [
{
"valudeid": "xxx-xx6262-xxxyyy-1111-11-11-1111"
}
]
},
"Itemdetails": [
{
"BoundId": "Value2",
"isexpired": true,
"FamilyCode": "PREMIUM",
"availabilityDetails": [
{
"travelID": "AAA-AB1234-AAABBB-2022-11-10-1111",
"quota": "X",
"scale": "XXX",
"class": "X"
}
]
}
]
}
]
},
"warnings": [
{
"code": "xxxx",
"detail": "xxxxxxxx",
"title": "xxxxxxxx"
}
]
}'''
def json = new groovy.json.JsonSlurper().parseText txt
List<String> BoundIds = json.data.RenewalDetails.Itemdetails*.find { itemDetail ->
itemDetail.availabilityDetails[0].travelID.size() == 33 && itemDetail.FamilyCode == 'PREMIUM'
}?.BoundId
assert BoundIds.toString() == '[Value1, Value2]'
Note, that you will get the BoundIds as a List
If you amend your code like this:
def json = new groovy.json.JsonSlurper().parse(prev.getResponseData()
you would be able to access the number of returned items as:
def size = json.data.RenewalDetails.size()
as RenewalDetails represents a List
Just add as many queries you want using Groovy's && operator:
find { itemDetail ->
itemDetail.availabilityDetails[0].travelID.length() == 33 &&
itemDetail.FamilyCode.equals('PREMIUM')
}
More information:
Apache Groovy - Parsing and producing JSON
Apache Groovy: What Is Groovy Used For?

jslt access parent field in for expression

Hi, I want to use jslt to transform json , but happen an unsolvable problem.
The input json data like this
{
"user_id": "001",
"friends": [{
"friend_id": "002"
}, {
"friend_id": "003"
}, {
"friend_id": "004"
}]
}
Then , what output json data I expected like the follow :
[{
"user_id": "001",
"friend_id": "002"
}, {
"user_id": "001",
"friend_id": "003"
}, {
"user_id": "001",
"friend_id": "004"
}]
In jslt expression , I use expression of for to traverse the array field friends :
[
for (.friends) {
"user_id": .user_id,
"friend_id": .friend_id
}
]
However , the treansform result can't get field user_id
[{
"friend_id": "002"
}, {
"friend_id": "003"
}, {
"friend_id": "004"
}]
How can I access field user_id out of the scope related array field friends ?
Looking forward for your help, thanks !
The other answer is correct, but more complex than it needs to be. This is enough:
let user_id = (.user_id)
[ for (.friends) { "user_id": $user_id , "friend_id" : .friend_id } ]
Note that if you really want to report errors you could do it like this:
if (.user_id and .friends)
let user_id = (.user_id)
[ for (.friends) { "user_id": $user_id , "friend_id" : .friend_id } ]
else if (not(.user_id))
error("user_id field missing")
else
error("friends field missing")
Using error turns this into an exception at the Java level.
The reason the parent operator is not supported is that Jackson doesn't have a parent pointer in its nodes. That's a performance feature, because it means the node can be reused several places, saving CPU and memory.
You'll need to use a variable for the user_id, which you can then reference in the loop.
The following should fit your requirement:
if (.user_id)
let user = .user_id
if (.friends)
[
for (.friends) {
"user_id": $user,
"friend_id": .friend_id
}
]
else
error("missing key 'friends'")
else
error("missing key 'user_id'")

Issue with cts.jsonPropertyScopeQuery and cts.jsonPropertyValueQuery with data types and field order

I have MarkLogic 9 on my database.
I have created the following documents in my database:
test1.json
{
"users": [
{
"userId": "A",
"value": 0
}
]
}
test2.json
{
"users": [
{
"userId": "A",
"value": "0"
}
]
}
test3.json
{
"users": [
{
"value": 0,
"userId": "A"
}
]
}
test4.json
{
"users": [
{
"value": "0",
"userId": "A"
}
]
}
I have run the following codes and have recorded the results:
cts.uris(“”, null, cts.jsonPropertyScopeQuery(
"users",
cts.andQuery(
[
cts.jsonPropertyValueQuery('userId', "A"),
cts.jsonPropertyValueQuery('value', "0"),
]
)
))
Result: test2.json, test4.json
cts.uris(“”, null, cts.jsonPropertyScopeQuery(
"users",
cts.andQuery(
[
cts.jsonPropertyValueQuery('userId', "A"),
cts.jsonPropertyValueQuery('value', 0),
]
)
))
Result: test3.json
I was wondering why test1.json did not return in the 2nd query while test3.json did. They both had the same values for fields but in different order. The order of the fields are different in test2.json and test4.json, however, the query returned both documents. The only difference between the 2 pairs that I can think of is that there are 2 data types for the field “value”, integer and string.
How would I go about resolving this issue?
https://docs.marklogic.com/cts.jsonPropertyValueQuery shows the value to match as an array.
If you want to keep the variants in data, maybe you can try something on the query side like cts.jsonPropertyValueQuery('value', ["0", 0])

TSQL JSON How to add an array to an existing Json Object?

We've got a SQL query where we create a JSON file with for JSON Path.
We want to merge 2 JSON objects into 1 JSON. But we struggle with the code how to accomplish this task.
We tried JSON_MODIFY to merge them together using append. But this did not work for us.
What we'd like to do is this, we have 2 seperate json objects and we want to merge them as one.
Json Object A:
{
"ID" : 0,
"Name" : "a name",
"Description" : "a description"
}
and Json Object B
"Nodes" : [
{
"NodeID" : 10,
"NodeName" : "Node 0"
},
{
"NodeID" : 11,
"NodeName" : "Node 1"
}
]
What we want to have:
{
"ID" : 0,
"Name" : "a name",
"Description" : "a description",
"Nodes" : [
{
"NodeID" : 10,
"NodeName" : "Node 0"
},
{
"NodeID" : 11,
"NodeName" : "Node 1"
}
]
}
Our current SQL Query looks like this:
set #JsonCourse = ( select c.name, c.id, c.description from dbo.courses c where c.id = #id for json path)
set #JsonNodes = ( select n.id, n.name from dbo.nodes n where n.courseId = #id for json path, root('Nodes'))
set #CompleteJson = JSON_MODIFY(#JsonCourse,'append $',JSON_QUERY(#JsonNodes));
print #CompleteJson
But our result is like this:
[
{
"ID" : 0,
"Name" : "a name",
"Description" : "a description"
},
{
"Nodes" : [
{
"NodeID" : 10,
"NodeName" : "Node 0"
},
{
"NodeID" : 11,
"NodeName" : "Node 1"
}
]
}
]
Note: we've used hypothetical data here.
How do we fix this with JSON_MODIFY?
So I'll add another answer, as this is a completely different thing as the first answer:
As I do not have your tables, I'll set the JSON variables to the values you provided
DECLARE #json1 NVARCHAR(MAX)=
N'{
"ID" : 0,
"Name" : "a name",
"Description" : "a description"
}'
DECLARE #json2 NVARCHAR(MAX)= --<-- had to add the surrounding {}, otherwise this was invalid JSON
N'{"Nodes" :
[
{
"NodeID" : 10,
"NodeName" : "Node 0"
},
{
"NodeID" : 11,
"NodeName" : "Node 1"
}
]}';
--We do not need append here.
--We have to tell the engine the name of the new node.
--To avoid repeated key Nodes I read from the #json2 using $.Nodes as path
DECLARE #CompleteJSON NVARCHAR(MAX)=JSON_MODIFY(#Json1,'$.Nodes',JSON_QUERY(#Json2,'$.Nodes'));
PRINT #CompleteJSON;
I hope this is closer to your needs...
Please read about creating a MCVE. This is a stand-alone sample, which makes your issue reproducible, and helps us to provide easy answers...
If I got this correctly there is a 1:n related structure, where each node in your "object A" can have several nodes in "object B".
My following code will simulate this through INFORMATION_SCHEMA. Each table as 1 or many columns.
We solve this with a correlated sub-query. This is the way to create nested JSON arrays:
SELECT TOP 3 t.TABLE_NAME AS NodeName
,t.TABLE_TYPE AS NodeType
,(
SELECT TOP 3 c.COLUMN_NAME AS ColumnName
,c.DATA_TYPE AS ColumnType
FROM INFORMATION_SCHEMA.COLUMNS c
WHERE c.TABLE_CATALOG=t.TABLE_CATALOG
AND c.TABLE_SCHEMA=t.TABLE_SCHEMA
AND c.TABLE_NAME=t.TABLE_NAME
FOR JSON PATH
) AS MyColumns
FROM INFORMATION_SCHEMA.TABLES t
FOR JSON PATH;
The result
[
{
"NodeName": "spt_fallback_db",
"NodeType": "BASE TABLE",
"MyColumns": [
{
"ColumnName": "xserver_name",
"ColumnType": "varchar"
},
{
"ColumnName": "xdttm_ins",
"ColumnType": "datetime"
},
{
"ColumnName": "xdttm_last_ins_upd",
"ColumnType": "datetime"
}
]
},
{
"NodeName": "spt_fallback_dev",
"NodeType": "BASE TABLE",
"MyColumns": [
{
"ColumnName": "xserver_name",
"ColumnType": "varchar"
},
{
"ColumnName": "xdttm_ins",
"ColumnType": "datetime"
},
{
"ColumnName": "xdttm_last_ins_upd",
"ColumnType": "datetime"
}
]
},
{
"NodeName": "spt_fallback_usg",
"NodeType": "BASE TABLE",
"MyColumns": [
{
"ColumnName": "xserver_name",
"ColumnType": "varchar"
},
{
"ColumnName": "xdttm_ins",
"ColumnType": "datetime"
},
{
"ColumnName": "xdttm_last_ins_upd",
"ColumnType": "datetime"
}
]
}
]
As you can see, each table as a nested set of columns, represented through a JSON array.

react native json image

I want to print out JSON images as a variable.
This is my local JSON file (JsonData.json):
{
"appetizer": [
{
"num": "appetizer1",
"name": "salad",
"condition": [ "1", "2" ],
"image": "./appetizer/salad.png"
},
{
"num": "appetizer2",
"name": "soup",
"condition": [ "2", "3" ],
"image": "./appetizer/soup.png"
},
…
],
"main": [
{
"num": "main1",
"name": "beef",
"condition": [ "1" ],
"image": "./main/beef.png"
},
{
"num": "main2",
"name": "fish",
"condition": [ "2", "3" ],
"image": "./main/fish.png"
},
…
]
}
I filtered the name when condition="2". (salad,soup,fish)
This is the code for filtering name:
const newArray1 = [...JsonData["apptizer"], ...JsonData["main"]];
const JsonResult = newArray1.filter(item => {
if(item.condition.indexOf("2") !== -1) return item.name;
});
AND I want to get the image when condition="2".
How can I get them? And How can I print out them?
Do I have to use base64? If so, Can you tell me how to use it?
I saw the explanation, but I can't understand it.
And I imported JSON file this way (I've been correctly using it):
var JsonData = require('./JsonData.json');
You can use below code:
let mainObject = JSON.parse(JSON.stringify(data))
let allKeys = Object.keys(mainObject)
let finalObject = []
allKeys.map((value, index) => {
let array = mainObject[value]
array.map((aryObject, aryIndex) => {
let condition = aryObject['condition']
if (condition.includes('2')) {
finalObject.push(aryObject)
}
})
})
alert(JSON.stringify(finalObject))
You can import data in top of screen:
import { data } from './data';
You can add below text in data.js:
export const data = {
"appetizer": [
{
"num": "appetizer1",
"name": "salad",
"condition": ["1"],
"image": "./appetizer/salad.png"
},
{
"num": "appetizer2222",
"name": "soup",
"condition": ["2", "3"],
"image": "./appetizer/soup.png"
},
],
"main": [
{
"num": "main1",
"name": "beef",
"condition": ["1"],
"image": "./main/beef.png"
},
{
"num": "main2",
"name": "fish",
"condition": ["21", "3"],
"image": "./main/fish.png"
},
]
}
You can use Object#values to get the arrays corresponding to appetizer and main and then Array#flat to extract the nested objects into a transformed array. Then use the Array#filter (which you are already using) to filter out only the required objects based on your condition and then Array#map to get the name and image values out of every filtered object into an array of objects.
Please consider following snippts
const jsonData = {"appetizer":[{"num":"appetizer1","name":"salad","condition":["1","2"],"image":"./appetizer/salad.png"},{"num":"appetizer2","name":"soup","condition":["2","3"],"image":"./appetizer/soup.png"}],"main":[{"num":"main1","name":"beef","condition":["1"],"image":"./main/beef.png"},{"num":"main2","name":"fish","condition":["2","3"],"image":"./main/fish.png"}]};
const filteredValues = Object.values(jsonData)
.flat()
.filter(o => o.condition.includes('2'))
.map(({name, image}) => ({ name, image }));
console.log(filteredValues);
The output of the above code will be an array of objects having the following structure
[{
"name": SOME_NAME,
"image": SOME_PATH
},
{
"name": SOME_NAME,
"image": SOME_PATH
},
...
]
You can use the above array to retrieve your image path and display it accordingly.
I think you shouldn't be worried about base64 as images are stored locally and path will be sufficient to display the image.
Hope this will help!!!
Side Note: You can avoid the Array#flat part as you are already doing it manually [...JsonData["apptizer"], ...JsonData["main"]] but flat will be handy in case there are more keys in jsonData that need to be considered.