Deep Copy using Jackson: String or JsonNode - java

Objective: deep copy (or clone) of a Java object
One of the suggested ways (almost everywhere) to do it is using Jackson:
MyPojo myPojo = new MyPojo();
ObjectMapper mapper = new ObjectMapper();
MyPojo newPojo = mapper.readValue(mapper.writeValueAsString(myPojo), MyPojo.class);
Question: is the following better? in terms of performance? is there any drawbacks?
MyPojo myPojo = new MyPojo();
ObjectMapper mapper = new ObjectMapper();
MyPojo newPojo = mapper.treeToValue(mapper.valueToTree(myPojo), MyPojo.class);

Answered by Tatu Saloranta:
Second way should be bit more efficient since it only creates and uses
logical token stream but does not have to encode JSON and then decode (parse) it to/from token stream. So that is close to optimal regarding
Jackson.
About the only thing to make it even more optimal would be to directly
use TokenBuffer (which is what Jackson itself uses for buffering). Something like:
TokenBuffer tb = new TokenBuffer(); // or one of factory methods
mapper.writeValue(tb, myPojo);
MyPojo copy = mapper.readValue(tb.asParser(), MyPojo.class);
This would further eliminate construction and traversal of the tree model. I don't know how big a difference it'll make, but is not much more code.
Thanks Tatu :)

I have copied my "valueNode" during iteration and made a copy of it using ObjectMapper now "copyJsonNode" is the replica of "valueNode" which i need for further implementation.
if (entry.getKey().equalsIgnoreCase("admin")) {
JsonNode valueNode = entry.getValue();
String copyjson = valueNode.toString();
ObjectMapper objectMapper = new ObjectMapper();
JsonNode copyJsonNode = objectMapper.readTree(copyjson);
....}

Related

Jackson Unmarshall custom object instead of LinkedHashMap

I have a Java object Results:
public class MetaData {
private List<AttributeValue<String,Object>> properties
private String name
...
... getters/setters ...
}
The AttributeValue class is a generic key-value class. It's possible different AttributeValue's are nested. The (value) Object will then be another AttributeValue and so forth.
Due to legacy reasons the structure of this object cannot be altered.
I have my JSON, which I try to map to this object.
All goes well for the regular properties. Also the first level of the list is filled with AttributeValues.
The problem is the Object. Jackson doesn't know how to interpret this nested behavior and just makes it a LinkedHashMap.
I'm looking for a way to implement custom behavior to tell Jackson this has to be a AttributeValue-object instead of the LinkedHashMap.
This is how I'm currently converting the JSON:
ObjectMapper om = new ObjectMapper();
MetaData metaData = om.readValue(jsonString, new TypeReference<MetaData>(){});
And this is example JSON. (this is obtained by serializing an existing MetaData object to JSON, I have complete control over this syntax).
{
"properties":[
{
"attribute":"creators",
"value":[
{
"attribute":"creator",
"value":"user1"
},{
"attribute":"creator",
"value":"user2"
}
]
},{
"attribute":"type",
"value": "question"
}
],
"name":"example"
}
(btw: I've tried the same using GSON, but then the object is a StringMap and the problem is the same. Solutions using GSON are also welcome).
edit In Using Jackson ObjectMapper with Generics to POJO instead of LinkedHashMap there is a comment from StaxMan:
"LinkedHashMap is only returned when type information is missing (or if Object.class is defined as type)."
The latter seems to be the issue here. Is there a way I can override this?
If you have control over the serialization, try calling enableDefaultTyping() on your mapper.
Consider this example:
Pair<Integer, Pair<Integer, Integer>> pair = new Pair<>(1, new Pair<>(1, 1));
ObjectMapper mapper = new ObjectMapper();
String str = mapper.writeValueAsString(pair);
Pair result = mapper.readValue(str, Pair.class);
Without enableDefaultTyping(), I would have str = {"k":1,"v":{"k":1,"v":1}} which would deserialize to a Pair with LinkedHashMap.
But if I enableDefaultTyping() on mapper, then str = {"k":1,"v":["Pair",{"k":1,"v":1}]} which then perfectly deserializes to Pair<Integer, Pair<...>>.

Putting a value in DynamoDB table with attributes as set of objects

I need to persist an attribute in DynamoDB Table, whhere attribute is Set<Some Class>.
Basically I need to put that attribute in Map<String,AttributeValue>.
For generation situation we generally do
Map<String,AttributeValue> itemsInTable = new HashMap<String, AttributeValue>();
itemsInTable.put("Id", new AttributeValue("123"));
Now suppose I have a Set<Info> where Info is a class.
How to put that kind of Attribuute?
and also if the attribute Value is a list ??
After two three days of research I just found there is no direct way to put something like set<Info> in DynamoDB using this kind of Notation.
Map<String,AttributeValue> itemsInTable = new HashMap<String, AttributeValue>();
itemsInTable.put("Id", new AttributeValue("123"));
But we can opt some middle way to convert the Object to JSONObject or JSONString and put in Map<String,AttributeValue> by using
Set<String> set = new HashSet<String>();
Map<String,AttributeValue> itemsInTable = new HashMap<String, AttributeValue>();
itemsInTable.put("Id", new AttributeValue().withSS(set));
To convert object to jsonString we can take help of jackson library.
import this:
import zaaa.org.codehaus.jackson.map.ObbjectMapper
Define ObjectMapper
private final ObjectMapper jacksonObjectMapper = new ObjectMapper();
Now suppose class is Container and its corresponding Object is `container'
Now the main job is to convert it into jsonString which can be easily done by using writevalueAsString function.
String jsonString = jacksonObjectMapper.writeValueAsString(container)
Now we have jsonString, we can easily convert it to JSONObject or JSONArray whenever needed.
I solved my issue by using this way.

Can't parse JSON array of arrays to LinkedHashMap in Jackson

I' m developing an Android REST client. We use JSON as data exchange format, so I use a Jackson parser. I get different Json responses from the server like simple arrays:
{"user_id":"332","user_role":"1"}
or something else. All these stuff I parse to LinkedHashMap<String, Object> and everything works perfectly but when I got this response from the server:
[ { "user_id":"352",
"user_role":"expert",
"name":"Test 12-18",
"description":"Test" },
{ "user_id":"263",
"user_role":"novice lab",
"name":"Tom's Desk",
"description":"Desk"}
]
I got null: {} after parsing.Here is my code where i use Jackson:
ObjectMapper mapParametersToJSON = new ObjectMapper();
String serverResponseBody = responseFromServer.getBody();
LinkedHashMap<String, Object> resultofOperation = new LinkedHashMap<String,
Object>();
TypeReference<LinkedHashMap<String,Object>> genericTypeReferenceInformation = new
TypeReference<LinkedHashMap<String,Object>>() {};
try {
resultofOperation = mapParametersToJSON.readValue(serverResponseBody,
genericTypeReferenceInformation);
So, why Jackson failed to parse this? How can I fix this?
Others have suggested the problem, but solutions are bit incomplete. If you need to deal with JSON Objects and Arrays, you can either bind to java.lang.Object, check the type:
Object stuff = objectMapper.readValue(json, Object.class);
and you will get either List or Map (specifically, ArrayList or LinkedHashMap, by default; these defaults can be changed).
Or you can do JSON trees with JsonNode:
JsonNode root = objectMapper.readTree(json);
if (root.isObject()) { // JSON Object
} else if (root.isArray()) { ...
}
latter is often more convenient.
One nice thing is that you can still create regular POJOs out of these, for example:
if (root.isObject()) {
MyObject ob = objectMapper.treeToValue(MyObject.class);
}
// or with Object, use objectMapper.convertValue(ob, MyObject.class)
so you can even have different handling for different types; go back and forth different representations.
The first JSON in your question is a map, or an object. The second is an array. You're not parsing an array, you're parsing a map.
You need to do something like this:
List<MyClass> myObjects = mapper.readValue(jsonInput, new TypeReference<List<MyClass>>(){});
Almost identical question with answer here.
In JSON the {"key": "value"} is Object and the ["this", "that"] is Array.
So, in case when you're receiving the array of objects you should use something like List<Map<Key, Value>>.
You are facing an error, because [] construction can't be translated into Map reference, only in List or array.
I would recommend do it something in this way:
ObjectMapper objectMapper = new ObjectMapper();
List<Map<String,String>> parsedResult = objectMapper.reader(CollectionType.construct(LinkedList.class, MapType.construct(LinkedHashMap.class, SimpleType.construct(String.class), SimpleType.construct(String.class)))).readValue(serverResponseBody);
//if you need the one result map
Map<String, String> resultMap = new LinkedHashMap<String, String>();
for (Map<String, String> map: parsedResult){
resultMap.putAll(map);
}

how to get 2 values from a nested Json string with jackson

I m using jackson library to deserialize from Json. I need to extract only 2 value from this json i.e. c1 and d1. I have used this code... I need to know the correct approach to overcome get c1 and d1 values...
My json
{"Alpha":{"A":{"B":{"C":{"c1":1234,c2:"abcd"},"D":{"d1":"xyz","d2":5678,"d3":"qwerty"},"E":[{"e1":456,"e2":"mnop"},{"e1":098,"e2":"qrst"}]}}},"X"{"x1":8098}}
ObjectMapper mapper = new ObjectMapper();
mainclass alphaobj = mapper.readValue(new File("C:\\employee.json"), mainclass.class);
System.out.println(alphaobj.A.B.C.getc1());
Maybe you should use Jackson Tree Model instead?
Something like:
JsonNode root = mapper.readTree(file);
int c1 = root.path("Alpha").path("A").path("C").path("C1").intValue();
and so on.

readValue and readTree in Jackson: when to use which?

I'm just starting using the Jackson JSON library. Jackson is a very powerful library, but it has a terribly extensive API. A lot of things can be done in multiple ways. This makes it hard to find your way in Jackson - how to know what is the correct/best way of doing things?
Why would I use this solution:
String json = "{\"a\":2, \"b\":\"a string\", \"c\": [6.7, 6, 5.6, 8.0]}";
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readValue(json, JsonNode.class);
if (node.isObject()) {
ObjectNode obj = mapper.convertValue(node, ObjectNode.class);
if (obj.has("a")) {
System.out.println("a=" + obj.get("a").asDouble());
}
}
Over a solution like this:
String json = "{\"a\":2, \"b\":\"a string\", \"c\": [6.7, 6, 5.6, 8.0]}";
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(json);
if (node.isObject()) {
ObjectNode obj = (ObjectNode) node;
if (obj.has("a")) {
System.out.println("a=" + obj.get("a").asDouble());
}
}
Or over solutions that I came across using JsonFactory and JsonParser and maybe even more options...
It seems to mee that mapper.readValue is most generic and can be used in a lot of cases: read to JsonNode, ObjectNode, ArrayNode, PoJo, etc. So why would I want to use mapper.readTree?
And what is the best way to convert a JsonNode to an ObjectNode? Just cast to ObjectNode? Or use something like mapper.convertValue?
readValue() can be used for any and all types, including JsonNode. readTree() only works for JsonNode (tree model); and is added for convenience.
Note that you NEVER want to use your first example: it is equivalent to writing out your node as JSON, then reading it back -- just cast it.
Read value can be used for your own java classes:
public class Foo {
private int a;
private String b;
private double[] c;
// getters/setters
}
String json = "{\"a\":2, \"b\":\"a string\", \"c\": [6.7, 6, 5.6, 8.0]}";
ObjectMapper mapper = new ObjectMapper();
Foo foo = mapper.readValue(json, Foo.class);
i.e. You may choose readTree when you do not know exact type of the Object, and readValue when you know the Object type for sure.
These methods have different runtime when you parsing same JSON
The fastest way is using readValue() with type reference HashMap:
TypeReference<HashMap<String, Object>> typeRef= new TypeReference<>() {};
HashMap<String, Object> parsedJson = objectMapper.readValue(json,typeRef);
Using readTree() and writing manual parser with JsonNode objects also wiil give you fast runtime.
Additional benefit of using readTree(): you can use stream parallel when parsing ArrayNode objects, which will boost runtime on multicore processors.
The slowest way is using readValue() with POJO class:
YourPOJO parsedJson = objectMapper.readValue(json, YourPOJO.class);
This is approximate runtime of each parsing way on same JSON:
Auto parsing raw map with ObjectMapper readValue() runtime: 34 ms
Auto parsing type reference map with ObjectMapper readValue() runtime: 6 ms
Auto parsing POJO with ObjectMapper readValue() runtime: 51 ms
Manual sequential parsing with ObjectMapper readTree() runtime: 18 ms
Manual stream parallel parsing with ObjectMapper readTree() runtime: 10 ms
I have done my example and tests here:
https://github.com/EvgenyUmansky/java-json-xml-processing/blob/master/src/test/java/JacksonTest.java
Another tests here:
https://gist.github.com/varren/3b7468a1d7abadad4958fe6bd7d42d44

Categories