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
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.
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.
A team I am working with has a lot of dirty data. Sometimes a field might be a string, sometimes an integer for a particular field in several related Mongo collections. When it's a string, we can always rely on it being a valid integer, so these two work just fine:
PUT index/1
{
"field": 1
}
PUT index/2
{
"field": "2"
}
However, the _source field in the latter returns "field" as a string. Cleaning up the source data is not an option for me because it's outside of my authority. So is there a way to have ElasticSearch return "field" consistently as an integer or am I always going to be stuck with getting it back as whatever form it was indexed (integer or string)?
If you're using ES 5+, you can use an ingest node, and an ingest pipeline which includes a convert processor.
There are have been numerous times where I have wanted a library that given some simple key value properties creates a tree of maps (and or lists) (a map containing maps.. think Java JSON) and or retrieves and sets values on an existing tree of maps.
An Example (property order is important):
a.b = "b"
a.c.d = 1
a.e.f[0] = "blah"
The above would be parsed into Map<String,?> where a is Map and c is a key in a with the value of a Map contains key of d with the value 1... etc.
A way to visualize this is as a JSON tree:
{
"a" :
{
"b" : "b",
"c" :
{
"d" : 1
},
"e" :
{
"f" :
["blah"]
}
}
}
I have ran into this many times of needing a simple object path like language and general expression languages will not work as the collections are not automatically created.
Consequently I have written my own simple split on . and recursively create maps but I am hoping that perhaps there is a well tested library that allows Java Object manipuluation/creation with out the overhead of a general purpose or turing complete expression language (ie Groovy and EL would be overkill).
I know that Spring has a DataBinder which does something analogous but it only works on POJOs and will not work on a general Map (this is because it needs to know how to convert the data which is not an issue here).
Here is another example of someone trying to do something similar albeit with Jackson: http://pastebin.com/F4feRr8y