This question already has answers here:
Query a JSONObject in java
(6 answers)
Closed 4 years ago.
I have some json data
{
"attributesMappings": [
{
"domainType": "WI",
"attribute": [
{
"staticAttributes": [
{
"attributeName": "test",
"attributeValue": "test",
"required": true
}
]
}
]
},
{
"domainType": "PI",
"attribute": null
}
]
}
I can read the object using
JSONArray vendorData = mainObj.getJSONArray("attributesMappings");
Suppose I want to get only the object where where domain type ="WI", I know it can be done using
JSONObject obj = vendorData.getJSONObject(0);
And then I can perform the manipulations, suppose I dont know at what index "WI" will be stored, is there a way of getting the data. I know we can iterate over the array items and match for domainType.
Can we do it in a way whereby using "WI" in the getJSONObject or something of that sort I can get the complete object.
JSONObject domainType = attributeMappings.getJSONObject("WI");
AFAIK, there is no more efficient way of searching a JSON object tree than iterating it. But the cost of searching will actually be small compared with the cost of parsing the object tree.
You could potentially do better than the "parse to JSONObject" using a stream-based parser, and coding your parse event handlers to look for the information you are trying to extract. If the information you are looking for is near the beginning of the JSON serialization, you could save time by abandoning the parse as soon as you get a search "hit".
If you only doing one search of the JSON, that is the end of the story.
If you are going to search the same JSON repeatedly, then the way to get better performance is:
Parse the JSON tree, or map it to a POJO tree
Construct a separate index data structure for the in-memory tree.
Use the index to speed up searches.
So, in your example you might build an index for all attribute mappings based on the domainType field.
Alternatively, extract just the information you want into a data structure that is designed for your needs.
There are libraries around that do the equivalent of XQuery and XPath for JSON. This approach is definitely more convenient than writing a bunch of iteration code; e.g. (from #cricket_007's comment):
The JSONPath query [for your example] would be $.attributesMappings[?(#.domainType == "WI")]
For more information: Query a JSONObject in java
However, I would be hesitant to use "JSON query" libraries if you are looking for a more efficient solution.
Related
I have the following JSON list:
[
{
"name": "John Doe The First",
"age": 36
},
{
"name": "John Doe The Second",
"age": 10
}
]
And the following Java record:
public record Person(String name, int age) {}
Is it possible to Jackson not parse JSON entries matching a condition such as age under 18 without writing a Consumer and filtering out these JSonNodes as indicated in this answer?
What I want is described in the third and, until now last, comment of this very answer.
EDIT:
Based on Hiran Chaudhuri's answer it makes perfect sense that is impossible to the parser to not parse the entire file which leads me to a second question. Isn't possible to Jackson, once the JsonNode is converted into a POJO, to filter these ones out based on the described conditions?
Something similar to what #JsonFilter does but not forcing developers to write a filter based on the Stream backed by the array returned nor creating a whole new filtered list, the array returned would contain just what is needed.
What you are asking is to filter the entries returned from parsing the file, but you want to perform the filtering before the parser (Jackson) has had a chance to read the file. This is not possible.
You would have to scan the inputstream and detect the boy has the right age. If not, you'd want to skip that entry and perform parsing on the next entity. But would you know how many bytes to skip? All that is the work of a parser.
You you'd better parse the JSON and perform your filtering afterwards before the application starts processing it.
I need to parse json with a list of inhomogeneous "items" i.e. each may have different keys/structure but they share one common key (here called "a") that gives the type of the item.
{
"items":[
{"a":1, "d":2},
{"a":2, "b":{"c":2}}
]
}
One way I thought to do this might be to pick out the json string for each "item" from the list at path "$.items" using something like the following,
List<String> jsonStrings = JsonPath.parse(json).read("$.items");
such that the first string would be '{"a":1, "d":2}' and the second would be '{"a":2, "b":{"c":2}}'. This is so that I can continue to ask questions of the inner bits using JsonPath itself. Is this possible? (The code above fails as JsonPath returns a list of maps instead.)
An alternative solution might be to use a JsonPath "query path" (my term) to return only "items" with e.g. a = 2 as a list of maps - or a list of typed objects that match the nested structure of each item type, (perhaps sharing a super interface containing the type key as a field). Is this possible?
$.items doesn't work because it's not a list of strings. It's an array of JSON objects.
I'm not sure exactly what you're looking for, but there are several ways to handle it:
$.items.[*].a
Will return all values of a. This might be okay if everything has an a:
[1, 2]
If you want to get something with a specific value for a, you can use the following syntax:
$.items[?(#.a == 2)]
This returns:
[
{
"a": 2,
"b": {
"c": 2
}
}
]
See the JSONPath Github page for other examples.
I am working with a nasty API that returns complex JSON (more than 4200 lines) which includes multi dimensional arrays.
Some Objects are repeated,
in different locations of JSON.
For example:
"User":{
"$id": "9",
"Code": "NU",
"DisplayName": "My Name",
"Experience": 2.41
},
Is there an easy way to parse entire JSON file and find list of Users?
Sometimes User is on the top level and sometimes it is nested in a four dimensional array.
Short Answer
No,
you will need to do work to achieve your goal.
Some Details
This problem is not hopeless.
You can use an event-based JSON parser and ignore everything that is not related to the User object.
Here is a related Stack Overflow question with answers.
Also,
try a google search for "json sax parser java" and you will find about a million links.
Another option might be to use JSON Xpaths.
Try a google search for "json xpath java".
I have Java application which gets JSON strings like this one
{
"name": "testName",
"message": [
"TestMessage."
]
}
What I need is to check if there is a "message" array node which has value of "TestMessage." in any of it's elements.
I have access to com.jway.jsonpath.JsonPath in my code. But it looks that it can only read value of particular node. E.g. like this I will be able to fetch first element of "message" array
JsonPath.read(content, "$.message[0]")
But in this way I will have to fetch all elements of the array into Java and validate them there.
Instead I would like to perform this check by JsonPath engine itself.
How can I achive this goal with JsonPath? Is it better to use other library?
You can use the filter operations.
Filters are logical expressions used to filter arrays. A typical filter would be [?(#.age > 18)] where # represents the current item being processed. More complex filters can be created with logical operators && and ||. String literals must be enclosed by single or double quotes ([?(#.color == 'blue')] or [?(#.color == "blue")]).
like this:
JsonPath.read(json, "$.message[?(# == "TestMessage.")]");
more can refer the readme.MD of https://github.com/json-path/JsonPath
An example JSON.
EDIT: I've put it on a pastebin because of how big the file is - http://pastebin.com/wdR2paBp
How would I get an Array of "objects", then iterate through it and get the name (i.e. "minecraft/sounds/dig/sand4.ogg") and the hash from each of these files?
My attempts:
FileReader fr = new FileReader(location.getAbsolutePath());
JSONArray iIndexes = (JSONArray) parser.parse(fr);
I've also tried making Objects a JSONObject then making it a JSONArray then using a for loop to get every object, but I get a NPE or a ClassCast Exception (for the atttempt before this one).
What you have there is a JSON object, not a JSON array. Conceptually, you can treat it as a set of name/value pairs, but according to the JSON specification, the NV pairs are not ordered; i.e. it is a set, not a list.
There is no standard way to turn that JSONObject into a JSONArray. Casting won't work, and the JSONObject doesn't have a method to do the conversion. And certainly, there is no way using JSONSmart to preserve the apparent order of the NV pairs in your source file / string. (Which is a good thing, IMO, because the order shouldn't mean anything.)
If you want to iterate the NV pairs, the best way to do it is to use the entrySet method to get the JSONObject's entries as a Set ... and then iterate the set. (The JSONSmart version of JSONObject is a subclass of HashMap.)
Now if set of entries in "objects" is supposed to be ordered, then you have designed your JSON scheme incorrectly. You should be using a JSON array (using the [...] JSON syntax) and the elements need to be restructured as objects; e.g.
[ {
"name": "realms/lang/de_DE.lang",
"hash": "10a54fc66c8f479bb65c8d39c3b62265ac82e742",
"size": 8112
},
{
"name": "realms/lang/cy_GB.lang",
"hash": "14cfb2f24e7d91dbc22a2a0e3b880d9829320243",
"size": 7347
},
etcetera
]
When you parse that using JSONSmart you will get a JSONArray.