How to deep select Nodes based on given depth in Java? - java

I have json representation like below:
{
"total": "555",
"offset": "555",
"hasMore": "false",
"results": [
{
"associations": {
"workflowIds": [],
"companyIds": [],
"ownerIds": [],
"child": {
"name" : "association1",
"key" : "a1"
},
"quoteIds": [],
"contentIds": [],
"dealIds": [],
"contactIds": [
4646915
],
"ticketIds": []
},
"scheduledTasks": [
{
"taskType": "REMINDER",
"portalId": 214129,
"engagementType": "TASK",
"engagementId": 6604524566,
"timestamp": 1586815200000
}
]
},
{
"associations": {
"workflowIds": [],
"companyIds": [],
"ownerIds": [],
"quoteIds": [],
"contentIds": [],
"child": {
"name" : "association2",
"key" : "a2"
},
"dealIds": [],
"contactIds": [
4646915
],
"ticketIds": []
}
},
{
"associations": {
"workflowIds": [],
"companyIds": [],
"ownerIds": [],
"quoteIds": [],
"contentIds": [],
"dealIds": [],
"child": {
"name" : "association3",
"key" : "a3"
},
"contactIds": [
3995065
],
"ticketIds": []
}
},
{
"associations": {
"workflowIds": [],
"companyIds": [],
"ownerIds": [],
"quoteIds": [],
"contentIds": [],
"dealIds": [],
"contactIds": [
4648365
],
"ticketIds": []
}
}
]
}
I would like to get filtered information (something like sql) of given node by passing node selector string , to achieve this I am doing like below:
ObjectMapper objectMapper = new ObjectMapper();
JsonNode root = objectMapper.readTree(new File("/Users/pra/automation/foo.json"));
String result = root.at("/results/0/associations/0/child").toString();
Assert.assertNotNull(result);
and this code is working as well, it filters first nodes out of array because 0 level index is passed, but I need output for all matching elements, so as to achieve that I passed * instead of 0 but it is not working.
means i am trying something like below ( which is failing ):
String result = root.at("/results/*/associations/*/child").toString();
Desired Output that needed:
[
{
"name" : "association1",
"key" : "a1"
},
{
"name" : "association2",
"key" : "a2"
},
{
"name" : "association3",
"key" : "a3"
}
]
I am open for other java based alternatives to achieve this. Thanks.

If you may switch to Gson library you may use im.wilk.vor:Voritem library gitHub or in Maven repository.
VorItem vorItemFactory = VorItemFactoryBuilder.standard.build();
JsonElement je = JsonParser.parse(...)
VorItem vi = vorItemFactory.from(je);
VorItem result = vorItemFactory.empty();
for (int idx = 0; idx < vi.get("result").list().size(); idx++) {
result.get(idx).set("name", vi.get("result").get(idx).get("associations/child/name");
result.get(idx).set("key", vi.get("result").get(idx).get("associations/child/key");
}
return result.asJsonElement().toString();

I found Andreas suggestion to be very effective and usage friendly, I was able to achieve desired output by using JSONPath library .
Usage code is as follows:
ObjectMapper objectMapper = new ObjectMapper();
String root = objectMapper.readTree(new File("/Users/pramo/automation/foo.json")).toString();
List<Object> associations = JsonPath.read(root, "$.results[*].associations.child");

Related

MongoDB - spring data - how to pick objects from different arrays into array / list

Object sample:
[
{
"name": "aaa",
"list": [
{
"key": "val1"
},
{
"key": "val2"
},
{
"key": "val3"
},
{
"key": "val4"
}
]
},
{
"name": "bbb",
"list": [
{
"key": "val2"
},
{
"key": "val4"
},
{
"key": "val6"
},
{
"key": "val8"
}
]
}
]
Query: list.key = val1 or val6
Actual results:
[
{"key":"val1"},
{"key":"val2"},
{"key":"val3"},
{"key":"val4"},
{"key":"val2"},
{"key":"val4"},
{"key":"val6"},
{"key":"val8"}
]
Expected results:
[
{"key":"val1"},
{"key":"val6"}
]
I need to pick all objects in list that equal to criteria.
#Query(value="{$or :{ 'listKey' : ?0},{ 'listKey' : ?1} }", fields="{ 'listKey' : 1}")
public List<Object> findByListKey(String value,String value2); // val1 or val6
Actually, it retrieves all objects of list in case it contains this value.
Any suggestions?
You need to project array documents using $ operator & for that you need to use $elemMatch in query.
Use this query
#Query(value="{ list: {$elemMatch: {$or: [{ 'key': ?0 }, { 'key': ?1 }]}}}", fields="{ 'list.$':1}")

Trying to get the name of the object whose collection array contains 'random'

I'm trying to get the names of the object whose collection array contains the word 'random'. Have tried various json path queries but couldn't get the right one.
{
"elements": [
{
"name": "My first element",
"language": "French",
"tags": ["Paris", "baguette", "Eiffel tower"]
},
{
"name": "randomOne",
"language": "Gibberish",
"tags": ["random", "plant, bag"]
},
{
"name": "bliep",
"language": "English",
"tags": ["lamp", "table, bed, oven"]
}
]}
Try this :
const jsonObj = {
"elements": [{
"name": "My first element",
"language": "French",
"tags": ["Paris", "baguette", "Eiffel tower"]
},
{
"name": "randomOne",
"language": "Gibberish",
"tags": ["random", "plant, bag"]
},
{
"name": "bliep",
"language": "English",
"tags": ["lamp", "table, bed, oven"]
}
]
};
let obj = jsonObj.elements.find((obj) => obj.tags.includes('random'));
console.log(obj.name); // randomOne
You can simply:
Loop through elements with Array.prototype.forEach()
Find instances containing "random" with Array.prototype.includes()
Add found names to result array with Array.prototype.push()
See below:
const jsondata = {
elements: [{
name: "My first element",
language: "French",
tags: ["Paris", "baguette", "Eiffel tower"]
},
{
name: "randomOne",
language: "Gibberish",
tags: ["random", "plant, bag"]
},
{
name: "bliep",
language: "English",
tags: ["lamp", "table, bed, oven"]
}
]
};
const result = [];
jsondata.elements.forEach(elem => {
if (elem.tags.includes("random")) {
result.push(elem.name);
}
});
console.log(result); // [ 'randomOne' ]
I have tried this question and able to get the names whose having 'random' in collection array.
$.elements[?(#.tags.indexOf('random') != -1)].name
Please update if it serves your purpose.

ObjectMapper readerForUpdating to replace array

I have two different JSON files.
File A:
{
"label": "A",
"links": [
{
"url": "urla"
}
]
}
File B:
{
"links": [
{
"url": "urlb"
}
]
}
Now I want to update A with the contents of B to get the following result JSON:
{
"label": "A",
"links": [
{
"url": "urlb"
}
]
}
That is the links array should be fully replaced with the contents of B.
But instead it merges the two Arrays:
{
"label": "A",
"links": [
{
"url": "urla",
"url": "urlb"
}
]
}
This is not desired.
The code for the merged file:
JsonNode A = ... // resolved from a service call
JsonNode B = ... // resolved from a service call
ObjectMapper mapper = new ObjectMapper();
result = mapper.readerForUpdating(A).readValue(B);
I also tried to set mapper.setDefaultMergeable(false); but it didn't help.
I use com.fasterxml.jackson.core:jackson-core:jar:2.9.8
Can someone help me?

How to split JSON into Dataset rows?

I have the following JSON input data:
{
"lib": [
{
"id": "a1",
"type": "push",
"icons": [
{
"iId": "111"
}
],
"id": "a2",
"type": "pull",
"icons": [
{
"iId": "111"
},
{
"iId": "222"
}
]
}
]
I want to get the following Dataset:
id type iId
a1 push 111
a2 pull 111
a2 pull 222
How can I do it?
This is my current code. I use Spark 2.3 and Java 1.8:
ds = spark
.read()
.option("multiLine", true).option("mode", "PERMISSIVE")
.json(jsonFilePath);
ds = ds
.select(org.apache.spark.sql.functions.explode(ds.col("lib.icons")).as("icons"));
However the result is wrong:
+---------------+
| icons|
+---------------+
| [[111]]|
|[[111], [222...|
+---------------+
How can I get the correct Dataset?
UPDATE:
I tries this code, but it generates some extra combinations of id, type and iId that do not exist in the input file.
ds = ds
.withColumn("icons", org.apache.spark.sql.functions.explode(ds.col("lib.icons")))
.withColumn("id", org.apache.spark.sql.functions.explode(ds.col("lib.id")))
.withColumn("type", org.apache.spark.sql.functions.explode(ds.col("lib.type")));
ds = ds.withColumn("its", org.apache.spark.sql.functions.explode(ds.col("icons")));
As already pointed out, the JSON String seems to be malformed. with the updated one, you can use the following to get result you wanted:
import org.apache.spark.sql.functions._
spark.read
.format("json")
.load("in/test.json")
.select(explode($"lib").alias("result"))
.select($"result.id", $"result.type", explode($"result.icons").alias("iId"))
.select($"id", $"type", $"iId.iId")
.show
Your JSON appears to be malformed. Fixing the indenting makes this slightly more apparent:
{
"lib": [
{
"id": "a1",
"type": "push",
"icons": [
{
"iId": "111"
}
],
"id": "a2",
"type": "pull",
"icons": [
{
"iId": "111"
},
{
"iId": "222"
}
]
}
]
Does your code work correctly if you feed it this JSON instead?
{
"lib": [
{
"id": "a1",
"type": "push",
"icons": [
{
"iId": "111"
}
]
},
{
"id": "a2",
"type": "pull",
"icons": [
{
"iId": "111"
},
{
"iId": "222"
}
]
}
]
}
Note the inserted }, { just before "id": "a2" to break the object with duplicate keys into two, and the closing } at the very end which had previously been omitted.

Get sub-array from JSON

I parsing some data from a json file. Here is my JSON File.
[
{
"topic": "Example1",
"contact": [
{
"ref": [
1
],
"corresponding": true,
"name": "XYZ"
},
{
"ref": [
1
],
"name": "ZXY"
},
{
"ref": [
1
],
"name": "ABC"
},
{
"ref": [
1,
2
],
"name":"BCA"
}
] ,
"type": "Presentation"
},
{
"topic": "Example2",
"contact": [
{
"ref": [
1
],
"corresponding": true,
"name": "XYZ"
},
{
"ref": [
1
],
"name": "ZXY"
},
{
"ref": [
1
],
"name": "ABC"
},
{
"ref": [
1,
2
],
"name":"BCA"
}
] ,
"type": "Poster"
}
]
I can fetch and store data one by one. Like this one
JSONArray getContactsArray = new JSONArray(jsonObject.getString("contact"));
for(int a =0 ; a < getContactsArray.length(); a++)
{
JSONObject getJSonObj = (JSONObject)getContactsArray.get(a);
String Name = getJSonObj.getString("name");
}
1)Now, my question is there any way to get all name values for each array with single query.
2) Can I get all those values in an Array ?
Please correct me, if I am doing anything wrong. Thank you.
Iteration cannot be avoided here as org.json and other Json parsers as well provide random access to objects but not to their properties collectively (as a collection). So, you can't query something like "all name properties of all contact objects" unless you probably get a Json parser like Gson to unmarshall it that way.
But, that's too much to just avoid a for loop when you can definitely shorten the parse by making use of the appropriate API methods to avoid unnecessary object casts.
JSONArray contacts = jsonObject.getJSONArray("contact");
String[] contactNames = new String[contacts.length()];
for(int i = 0 ; i < contactNames.length; i++) {
contactNames[i] = contacts.getJSONObject(i).getString("name");
}
Better to use a json parser such as GSon or Jackson to marshall your json to a java object. Then you can write utitlity method in your java class to retrieve all the names in that object.
Try this:
Create JSONObject of your file and try to get array of all names and iterate it to get all values.
public static String[] getNames(JSONObject jo) {
int length = jo.length();
if (length == 0) {
return null;
}
Iterator i = jo.keys();
String[] names = new String[length];
int j = 0;
while (i.hasNext()) {
names[j] = (String) i.next();
j += 1;
}
return names;
}

Categories