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
Related
I have a JSON which has an attribute with its List type. Can someone help me on how to read the value in Apache Velocity template? Following is an example of JSON. The challenge is to read the list of University from JSON and iterate through it.
{
"StudentName":"XYZ",
"List<Univesity>": [
{
"Name": "NYU",
"City": "NY",
"Country":"US",
} ]
}
The solution is dependent upon the JSON library you use, but for many of them, the following code should work:
#set( $universities = $document.get('List<University>') )
#foreach( $university in $universities )
... do something ...
#end
The main point to note here is that you can call any Java method on the object you get.
Also, if the security uberspector is not present, for debugging purposes you can display the Java class name of any object in the context, for instance: $document.class.name should display, in your case, something like com.fasterxml.jackson.databind.node.ObjectNode.
i'm trying to send JSON object to my free marker, but i getting error while testing JUnit
here is my object
{
"filename": "test",
"orderId": "123435",
"orderDate": "23.09.2020г.",
"itemsCount": "4",
"items": [
{
"itemName": "ТВ Приставка 400",
"itemCount": "2 шт Х 400₽",
"itemSum": "800.00"
}
],
"totalSumm": "3000.00"
}
here is my error:
FreeMarker template error:
The value you try to list is an extended_hash+string (org.camunda.bpm.engine.impl.util.json.JSONArray wrapped into f.e.b.StringModel), thus you must specify two loop variables after the "as"; one for the key, and another for the value, like <#... as k, v>).
how could i resolve it?
items is a arrays of JSON objects
FreeMarker knows nothing about org.camunda.bpm.engine.impl.util.json.JSONArray, so it doesn't see it as a list-like thing (as a sequence, as it's called in FTL). So you can do one of these:
Using the objectWrapper Configuration setting, you teach FreeMarker how to treat JSONArray as a list. (You can find more about custom ObjectWrapper-s elsewhere.) Then you can just #list such objects, and use all the other operations applicable to sequences.
Or, you call the Java API-s of JSONArray from the template. That will be less convenient of course, but requires no prior investment.
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.
I’ve been trying to create a dynamic text-grabbing system to be able to translate future programs into different languages more easily. I’m fairly new to Java, so I’m not well versed on the data types that there are, but would it be possible to take a JSON object (using simple.json) and converting it into something that I could reference easily and concisely?
For example given a JSON string:
{
"name": "John Doe",
"country": "US",
"age": 25,
"family": {
"immediate": {
"spouse": "Johnette Doe",
"children": [
{
"name": "Jimbles Doe",
"age": "213"
}
]
}
}
}
How could I set my file up so that I could reference it like so:
JohnClass.family.immediate.children[0].name
and get a value in return?
Get Jackson (either use the maven dependency or download the jar): http://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind/2.5.0
Untested code below, and to be honest a effortless punt, just to help you sort it quickly.
Instantiate JSONMapper (ideally in a constructor):
ObjectMapper mapper = new ObjectMapper();
Convert to Hashmap (you could also convert to any other object):
Map map = mapper.readValue("JSON_STRING_HERE", new TypeReference<HashMap>(){});
Access like this:
map.get("family").get("immediate").get("children").get(0).get("name");
You could also just use JSONObject which also implements the Map interface, but if you get used to do it this way you'll know how to do it for other objects.
When Parsing JSON I normally just constuct an object and use the gsonlibrary to parse my String into that object.
However, I now find myself with a rather complex response which consists of many elements each with sub elements of objects and arrays and arrays of objects. It looks something like this...
{
"type": "thetype",
"object":{
"text": "texthere",
"moretext": "more here"
},
...,
...,
...,
...,
"fieldIwant": [
{
"object":"object!"
},
....
....
{
"object":"object!"
},
]
}
The thing is, I'm only really interested in fieldIwantand nothing else. Is there not a way in Java for me to just extract that field and work with it alone and not all this other dead weight I do not need?
According to this http://sites.google.com/site/gson/gson-design-document it looks like gson does this for you by default.
When you are deserializing a Json string into an object of desired type, you can either navigate the tree of the input, or the type tree of the desired type. Gson uses the latter approach of navigating the type of the target object. This keeps you in tight control of instantiating only the type of objects that you are expecting (essentially validating the input against the expected "schema"). By doing this, you also ignore any extra fields that the Json input has but were not expected.
In other words, it doesn't deserialize any of the fields you don't need. You should be good to go.
You can use the low level JsonParser API
JsonObject jsonObject = new JsonParser().parse(json).getAsJsonObject();
yourArray = new Gson().fromJson(jsonObject.get("fieldIwant"), yourArrayType);
Alternatively you can create an ExclusionStrategy to use with a GsonBuilder