I have a json string like this:
"files": {
"fileA.c": {
"size": 100
},
"fileB.txt": {
"size": 200
}
}
I want to extract the file names, {"fileA.c","fileB.txt"}, using JsonPath. Note that the number of files is unknown.
The problem is, I don't know whether the file name is a key or a value:
If it is a key...well I certainly don't know the key name because that's the information I want to extract.
If it is a value, then what is its key?
Can I use JsonPath to extract the file names? If so, how?
If JsonPath cannot do this, is there any Java library for Json that can achieve this?
In your example, fileA.c and fileB.txt are keys, you can get them by iterating on the key in the enclosing object (which is referenced by the key "files").
I don't think JSONPath is really appropriate (or even applicable) in this case, it is designed to access elements when you know the structure of the documents, which means that basically you know the keys. It would be much easier to simply use a JSON parser.
I would suggest you to modify your data structure, to something like this;
var data = {
"files": [
{
"name": "fileA.c",
"size": 100
},
{
"name": "fileB.txt",
"size": 200
},
{
"name": "fileC.txt",
"size": 50
}
]
};
When structured like this, you can use DefiantJS (http://defiantjs.com) to query for files which sizes are larger than 100...like this:
JSON.search(data, '//*[size >= 100]/name')
DefiantJS extends the global object JSON with the method "search", with which you can query JSON structure with XPath expressions.
To see a working example with your data, check out this fiddle:
http://jsfiddle.net/jRN22/
Related
I have a nested json structure and a list of json paths. I want to extract and return only those parts of json that are defined in the json paths.
For eg: Let's say the json contains:
{
"id": 1,
"user": {
"name": "ABCD",
"email": "abcd#email.com",
"phone": "1234"
}
}
And I've the following JsonPath's
$.id
$.user.name
$.user.phone
So the json returned will be
{
"id": 1,
"user": {
"name": "ABCD",
"phone": "1234"
}
}
I've tried multiple ways with Jayway's JsonPath but I only get the value, not the entire heirarchy(Eg: If I search for $.user.name then I get ABCD instead of {"user": {"name": "ABCD"}}).
I looked at Extracting a subset of attributes with JSONPath but it does not answer the question of return entire json structure.
Any ideas how do I approach this? I suspect that I would need to build Jackson JsonNode iteratively using the JsonPath, but couldn't build a solution.
Note: The example above contains a simple json structure. In reality, I suspect to have many nested structures(even arrays) inside json.
I am trying to understand what exactly is it to flatten a Json and Unflatten a Json. Any links or blogs would be helpful.
Also w.r.t the additional question, I am trying to deserialize a flattened json attribute into objectmapper object. The Json I am trying is of following format.
{
"MyUserID": "45345dfsf4545",
"attributes": {
"ArrayAttribute1[0].alertMessage": "You have consumed all of your data allowance",
"ArrayAttribute1[0].promoName": "MyPromoTest",
"ArrayAttribute2[0].showmorepromosbutton": "true",
"ArrayAttribute1[0].promoPrice": "P 149.00",
"userType": 1,
"Attribute1": "Jan 28 2016 . 3:09PM",
"Attribute1": "true",
"Attribute2.validityColor": "RED",
"Attribute2.subscriberBal": "P 29.5",
}
}
Found this repository(https://github.com/wnameless/json-flattener) to flatten and unflatten a json. Make sure you unflatten it to a String first before feeding it to ObjectMapper. Unfortunately did not find a specification that defined the differences.
Flattening means to put the JSON Object into a single-Hierarchy-level like structure also called flat structure i.e there are no child or parent objects, everything is a key-value pair. Nested Objects will be accessible by dot operator.
On the contrary Un-flattening means to put the flattened structure in to Mutli-level structure where there exists Parent-child structure.
You need to pass a un-flattened structure to Object-mapper to de-serialize it.
Following examples will help in understanding it better
Nested JSON Object
{ "a":
{ "b": 1,
"c": null,
"d": [false, true]
},
"e": "f",
"g": 2.3
}
Flattened JSON
{ "a.b": 1,
"a.c": null,
"a.d[0]": false,
"a.d[1]": true,
"e": "f",
"g": 2.3
}
Java Map like flattened JSON
{a.b=1, a.c=null, a.d[0]=false, a.d[1]=true, e=f, g=2.3}
Why flattening is required?
If you don't internal structure of JSON Object, it will be quite cumbersome to access all the elements inside hence simple solution is to put everything on the same level and deepness of Objects is depicted by dot operator.
So flattening makes it easier to access the internal elements.
Serialization and de-serialization can only happen on un-flattened JSONs.
I have some spring MVC based APIs which produces JSON/ XML which represents API output.
{
"data" :
{
"users": [
{
"id": "001",
"name: "abc1",
"type": {
"id": "P",
"name": "Permanent"
}
},
{
"id": "002",
"name: "xyz",
"type": {
"id": "C",
"name": "Contractor"
}
}
]
}
}
I'm passing a parameter with request as
url?fields=users.id, users.type.id
users.type.id is a sub-node in users node.
users node is an array.
Now, what I want to do is to filter those only properties and create the response based upon fields passed in the request.
So the response to above filter condition should be same structure and will only contain wanted fields with values.
I'm trying to build a flat map with keys with a dot notation so I won't lose the track to filter, then I'll rebuild the JSON again. I feel this approach is just unreasonable because Jackson has the .path and .with API's to check existing path. But the real challenge is to extract and create a new JSON which matches the response JSON.
I'm looking for some ideas to achieve this. I don't want to try any third party libs btw. I know some libs are there. I want to prefer Jackson way to do this.
Feel free to add or comment if you have a further ideas.
Given a Json, is it possible to use Jackson to only parse out a section of the message?
Say that the data I'm interested in is buried in a deep hierarchy of fields and I simply do not care about creating DTO classes for each and every class.
Given a very simplified scenario I'd like to model the Telephone class without knowing anything about the structure before it:
...{
"firstName": "John",
"lastName" : "doe",
"age" : 26,
"address" : {
"streetAddress": "naist street",
"city" : "Nara",
"postalCode" : "630-0192"
},
"phoneNumbers": [
{
"type" : "iPhone",
"number": "0123-4567-8888"
},
{
"type" : "home",
"number": "0123-4567-8910"
}
]
}....
I'm thinking something in the terms of using json-path together with deserializing just the parts I'm interested of. Some pseudo:
List<Telephone> phoneNrs = parse(".my.deep.structure.persons.phoneNumbers", List<Telephone.class>);
ObjectMapper mapper = new ObjectMapper();
JsonNode json = mapper.readTree("... your JSON ...");
Using the JsonNode object you can then call get("my").get("deep").get("structure") to get the node you want.
Once you got your hands on that node, a simple call to mapper.treeToValue(myDeepJsonNode, Telephone[].class) will get you your array ofTelephone. You can get a list using a TypeReference as well.
To get to your deep JsonNode you can also use the findValue and findPath methods.
The Javadoc:
https://fasterxml.github.io/jackson-databind/javadoc/2.2.0/com/fasterxml/jackson/databind/JsonNode.html
Yes, it is possible the way you have mentioned in the Pseudo code. "phoneNumbers" is a key and value returned can be passed on to Jackson deserialiying.
If the response is an array of maps then you can iterate through each one of them and use the yourResponseAsJSONObject.get('phoneNumbers') method to get the value and pass it on to Jackson
or use JsonPath as mentioned by #dimas
You can use JsonPath library. With this library you can map your JsonPath output directly into POJO's.
Pseudo:
List<Telephone> phoneNrs = JsonPath.parse(json).read("$.my.deep.structure.persons.phoneNumbers", List.class);
To do this efficiently with Jackson, use the Streaming API via the JsonParser class (http://fasterxml.github.io/jackson-core/javadoc/2.5/com/fasterxml/jackson/core/JsonParser.html).
This approach will allocate no additional memory and will not incur the cost of deserializing values for all of the skipped data. Since the code will be much longer and more difficult to read than using Jackson's ObjectMapper, only do this if profiling shows unacceptable GC activity or CPU usage during parsing.
You can skip all of the nodes that you are uninterested in until you hit the "phoneNumbers" key. Then you can call the readValueAs function to deserialize the array of phone number dictionaries like so readValueAs(new TypeReference<MyPhoneNumberType[]>()).
See also:
a tutorial on reading and writing with JsonParser: http://www.cowtowncoder.com/blog/archives/2009/01/entry_132.html
The main documentation: https://github.com/FasterXML/jackson-core
Let's say that we have the following json response:
{
"data":
[
{
"id": 1,
"name": "Pablo"
},
{
"id": 2,
"name": "Ernesto"
}
]
...
}
Where the data list could consist of many more objects. If I where to verify that no name field is set to null, what would be the Rest Assured way of doing this?
Now I'm using:
from(response.asString()).get("data");
to get a list of HashMaps, and then moving on from there for each entry. But I guess there is some other way that is more efficient?
Edit/Clarification: I am wondering if there is a way to do this without the creation of a list of maps?
i guess this is the best we can do
List<HashMap> data = from(response.asString()).get("data");
for(HashMap map: data){
if(map.get("name") == null){
// null name found
}
}
// No null name found
Ok, so perhaps I should have read the docs a bit more carefully, the easiest (and intended) way to retrieve e.g. all name values (as in my example) is to use JsonPath like so:
List<Object> names = from(response.asString()).get("data.name");
So there are no magical utility methods within the Rest-Assured library to do this except using JsonPath.from().get() and then to go from there.