deserialize lazylist using jackson - json

I have a object which uses a org.apache.commons.collections.list.LazyList for one of its fields, which is serialized ti JSON. The JSON looks like this:
"myObject": ["org.apache.commons.collections.list.LazyList", [
{
"attr1": "asdasd",
"attr2": 1234
}
]],
The object field looks like this:
List<MyObject> myObject = ListUtils.lazyList(new ArrayList(), {new MyObject()} as Factory)
However trying to deserialize the above JSON using a Jackson ObjectMapper fails, since it can't find a default constructor for a LazyList - which makes sense. But how can I specify how this field can be deserialized?
Error message:
No default constructor for [collection type; class org.apache.commons.collections.list.LazyList, contains [simple type, class foo.bar.MyObject]]
Bounty-constraints:
To collect the bounty, this question needs to be answered using a custom jackson deserializer - the custom deserializer must not be field specific! Hence no solution using custom implementations of a LazyList for a specific type will answer this question adequately.

The solution below worked on both List and Map collection objects, it might also work on yours.
#JsonDeserialize(contentAs=MyObject.class)
private List<MyObject> myObject = ListUtils.lazyList(new ArrayList(), {new MyObject()} as Factory);

Related

Custom deserializer for generic list

I'm trying to create a custom deserializer for generic lists. Lets say I get a json representation of class B:
public class B{
List<A> listObject;
}
where A is some other class which I see only at runtime. I'd like to create a deserializer that will be able to infer the type of listObject as list with inner type A and deserialize it as such instead of using the default hashmap deserializer.
I tried using contextual deserializer, similar to what was suggested here
and then adding it as a custom deserializer for List
addDeserializer(List.class, new CustomListDeserializer())
But I'm not sure how am I supposed to read the json and create the list in deserialize function (in the Wrapper example above it's pretty simple, you read the value and set it as a value field, but if my 'wrapper' is List, how do I read the values and add them?)
I tried using readValue with CollectionType constructed with constructCollectionType(List.class, valueType) but then I go into an infinite loop, since readValue uses the deserializer from which it was called.
Any ideas?
Thanks for the suggestion. I solved it by parsing the json as an array of inner generic type and then converting to list, as follows:
Class<?> classOfArray = Array.newInstance(valueType.getRawClass(), 0).getClass();
Object[] parsedArray = (Object[]) parser.getCodec().readValue(parser, classOfArray);
return Arrays.asList(parsedArray);

Why I parse json into a Java List, but not a Scala List?

I am attempting to parse a json object that contains a list. I am able to parse the list if the field is backed by a Java List, but it fails if the field is backed by a Scala list. What is the difference between parsing into a Scala List vs a Java List, and what do I have to change to be able to parse this into a Scala List?
object JsonParsingExample extends App {
val objectMapper = new ObjectMapper()
// This line succeeds.
objectMapper.readValue("""{"list": ["a","b"]}""", classOf[JavaList])
// This line fails.
objectMapper.readValue("""{"list": ["a","b"]}""", classOf[ScalaList])
}
case class JavaList() {
#JsonProperty(value = "list")
var myList: java.util.ArrayList[String] = null
}
case class ScalaList() {
#JsonProperty(value = "list")
var myList: List[String] = null
}
The error message I receive is:
com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of scala.collection.immutable.List, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information
Jackson doesn't know anything about Scala types by default (otherwise it would have to depend on scala-library). To teach it, use jackson-module-scala.
Because the scala.collection.immutable.List is actually an abstract class. Generally when you use List("a", "b", "c") is the object List.apply() which is coming from this line: https://github.com/scala/scala/blob/2.12.x/src/library/scala/collection/immutable/List.scala#L452 and that's actually an inner class (something called scala.collection.immutable.$colon$colon).

force jackson mapper to always add class type on writeValue without annotations

Is it possible to configure jackson to always add the type of the serialized object to the generated json output.
For example:
package org.acme;
class ClassA
{
String a;
String b;
}
and I want the generated json to be:
["org.acme.ClassA",{"a":"str1","b":"str2"}]
You can do that with enableDefaultTyping() of the ObjectMapper
e.g.
mapper.enableDefaultTyping(DefaultTyping.OBJECT_AND_NON_CONCRETE);
See ObjectMapper API
If your are free to change from Jackson and do not especially need the format to match the one your are showing you can try Genson http://code.google.com/p/genson.
For example if your requirement is to be able to deserialize interfaces or abstract classes based on the original type of the object you serialized you can do:
interface Entity {}
static class Person implements Entity {}
Genson genson = new Genson.Builder().setWithClassMetadata(true).create();
// json will be equal to {"#class":"my.package.Person"}
String json = genson.serialize(new Person());
// and now Genson is able to deserialize it back to Person using the information
// in the Json Object
Person person = (Person) genson.deserialize(json, Entity.class);
Another nice feature is the ability to define aliases for your classes, so you show less information in the json stream but also this allows you to do refactoring without worring of existing json streams (for example if you store it in a database).
Genson genson = new Genson.Builder().addAlias("person", Person.class).create();
// json value is {"#class": "person"}
String json = genson.serialize(new Person());
Have a look at the wiki.

rest template serialize/deserialize Map objects with variosu key , value pairs

I am using resttemplate with jackson to marshall/unmarshall java/json objects.
What would be the best strategy to serialize/deserialize
a Map that may contain key value pairs such that keys are strings and values could
be various types for example an ArrayList of custom objects
I did some research on this site and found the use of #JsonAnyGetter #JsonAnySetter
could be used in this situation, but wasnt sure of how to deserialize in the context
of resttemplate getforobject method. Would one have to write a custom httpmessageconverter
to accomplish the deserialization?
Thanks in advance.
We'll assume you have a response like this:
{ key1: "something", key2: 3}
You'll want to have a DTO that has those fields:
class CustomResponse {
private String key1;
private long key2;
}
Make sure you add getters and setters for the above.
Now make your request:
restTemplate.postForObject(url, requestObject, CustomResponse.class);
The request object can be either a DTO like the above or just use Arrays and Maps to construct the requestObject.
You should add this annotation to your response DTOs. This ensures that if there are fields in the response that don't map in your DTO, they will be ignored.
#JsonIgnoreProperties(ignoreUnknown = true)

Jackson JSON custom deserializer works bad with composite object

I have a problem with my custom JSON deserializer.
I use Jackson to map JSON to Java and back. In some cases I need to write my own mapping.
I have an object (filter), which contains a set of another object(metaInfoClass). I try to deserialize the filter with Jackson, but I implemented an own deserializer for the inner object.
The JSON looks like this:
{
"freetext":false,
"cityName":null,
"regionName":null,
"countryName":null,
"maxResults":50,
"minDate":null,
"maxDate":null,
"metaInfoClasses":
[
{
"id":31,
"name":"Energy",
"D_TYPE":"Relevance"
}
],
"sources":[],
"ids":[]
}
My deserializer just works fine, it finds all the fields etc.
The problem is, that somehow (no idea why) the deserializer gets invoked on the rest of the JSON string, so the sources token is getting processed, and so on.
This is very weird, since I don't want to deserialize the big object, but only the inner metaInfoClass.
Even more weird: the CollectionDeserializer class keeps calling my deserializer with the json string even after it is ended. So nothing really happens, but the method gets called.
Any idea?
Thanks a lot!
I was able to find a solution.
I modified the implementation (in the deserialize method) to use to following code:
JsonNode tree = parser.readValueAsTree();
Iterator<Entry<String, JsonNode>> fieldNameIt = tree.getFields();
while (fieldNameIt.hasNext()) {
Entry<String, JsonNode> entry = fieldNameIt.next();
String key = entry.getKey();
String value = entry.getValue().getTextValue();
// ... custom code here
}
So with this approach, it was parsing only the right piece of the code and it's working right now.