Serialization Of Polymorphic Array Failing - java

I have enabled Polymorphic serialization support by adding annotations on the base class. I am able to seriazlize an individual object successfully and it is writing the type information as part of serialized data. However, the same is not happening if I store the objects in a list and serialize it.
It seems this issue was fixed in 1.6.3 (http://jira.codehaus.org/browse/JACKSON-362)
I am using Jackson 2.3.2 and still facing the issue.
Does somebody know how to fix this?
Code:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY,property = "type")
#JsonSubTypes({#Type(value = Derived.class, name = "derived")})
public abstract class Base {
}
public class Derived extends Base {
private String field;
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
}
public class Test {
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Derived d = new Derived();
d.setField("Name");
Base b = d;
System.out.println(mapper.writeValueAsString(b));
List<Base> list = new ArrayList<Base>();
list.add(d);
System.out.println(mapper.writeValueAsString(list));
}
}
Output:
{"type":"derived","field":"Name"}
[{"field":"Name"}]
Thanks,
Praveen

The answer is at https://github.com/FasterXML/jackson-databind/issues/699
This is due to Java type erasure: when serializing a List, all Jackson see as a type is List (roughly equivalent to List). And since type Object does not have polymorphic type information (annotation), none will be written.
So this is not a bug in Jackson, but an unfortunate feature of Java Type Erasure.
It does not apply to arrays, since they retain element type information (arrays are not generic; arrays of different types are different classes, whereas generic typing is mostly compile-time syntactic sugar).
There are three main ways to deal with this:
pass full generic type using TypeReference (ObjectMapper has method like mapper.writerFor(new TypeReference<List<Base>>() { }).writeValue(....)
Sub-class List to something like public class BaseList extends ArrayList<Base>() { }, and pass that: this type WILL retain type information
Avoid using root-level List and Maps
I personally recommend doing (3), since this avoids all related problems with type erasure.
In my opinion JSON root value should always be a JSON Object, usually serialized to/from POJO.
Approach (2) will however work, and this is what most users do. It does require use of an additional helper class.
Approach (1) may or may not work; problem being that forcing type information does also affect actual value serialization. So while it will add type id, it may result in some properties not being serialized.

This problem can be solved by using arrays, instead of list (since list does type erasure):
for example, your above test-case could be written as:
public class Test {
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Derived d = new Derived();
d.setField("Name");
Base b = d;
System.out.println(mapper.writeValueAsString(b));
List<Base> list = new ArrayList<Base>();
list.add(d);
System.out.println(mapper.writeValueAsString(
list.toArray(new Base[list.size]) // <--This Part
));
}
}

I had the same issue with object array. Object[] doesn't carry type information but individual objects do. It's a shame that jackson doesn't handle that automatically.
Two possible solutions:
1. Typed array serialization works just fine:
Base[] myArray = Base[]{d};
mapper.writeValueAsString(myArray)
this will actually produce expected result as Base[] has type information.
I solved that my issue with custom serializer.
Serializer:
public class ObjectArraySerializer extends StdSerializer<Object[]> {
public ObjectArraySerializer(final Class<Object[]> vc) {
super(vc);
}
#Override
public void serialize(
final Object[] data,
final JsonGenerator gen,
final SerializerProvider provider) throws IOException {
gen.writeStartArray();
for (Object obj : data) {
gen.writeObject(obj);
}
gen.writeEndArray();
}
}
ObjectMapper configuration:
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(
Object[].class,
new ObjectArraySerializer(Object[].class));
objectMapper.registerModule(module);

Related

How to give a List of any object as parameter in Java?

I have several classes that are doing the same thing : iterates over a List in an object, and add each items in a private field.
I have two objects : MyCustomObject, that have several fields, and ResultOfQuery, where it has a field called data that is a List<Map<String, Object>>.
For example:
private List<MyCustomObject> myCustomObjectList = new LinkedList();
public void setMyCustomObject (ResultOfQuery resultOfQuery){
ObjectMapper objectMapper = new ObjectMapper();
if(resultOfQuery!= null) {
for (Map<String, Object> map : resultOfQuery.getData()) {
myCustomObjectList.add(objectMapper.convertValue(map,
MyCustomObject.class));
}
}
The problem is that I have other classes that does the exact same method, but with another object instead of MyCustomObject.
So I thought that a good idea would be that all of these classes should extends a class that contains this method, and as a parameter it should take first a resultOfQuery, then a list of any objects, and then a Class.
Does it sounds good, or there is a better way to achieve this?
Also, how to give a list of any object ? I tried List<?>, but this shows me the following error :
Error at list add line
You can achieve type safety with generics and inheritance. If you declare a base class having the common stuff like:
public class BaseClass<T> {
private List<T> tList = new LinkedList<>();
private final Class<T> classT;
public BaseClass(Class<T> classT) {
this.classT = classT;
}
public void setObject (ResultOfQuery resultOfQuery){
ObjectMapper objectMapper = new ObjectMapper();
if(resultOfQuery!= null) {
for (Map<String, Object> map : resultOfQuery.getData()) {
tList.add(objectMapper.convertValue(map, classT));
}
}
}
}
Then it is easy to extend it for each different type, like:
public class MyCustomObjectExtendedClass extends BaseClass<MyCustomObject> {
public MyCustomObjectExtendedClass() {
super(MyCustomObject.class);
}
}
I have renamed stuff because it was decoupled from the MyCustomObject.
You can use private List myCustomObjectList = new LinkedList();
Instantiating a class using a raw type (i.e. without a type parameter, as in List list = new ArrayList(3)), is something you shouldn't do, as it is less type-safe, and is only allowed for backwards compatibility.
Link: Java Generics List and ArrayList with and without Parameters

Jackon json string to Java object using generics

I've seen numerous posts related to my below query but unable to find exactly how to handle this issue:
I've a class as below:
public class MyClass<T> implements IMyInterface<String, T> {
#Override
public T myMethod(String jsonString) {
ObjectMapper mapper = new ObjectMapper();
//T result = mapper.readValue(jsonString, T.class) //for sure T.class does not make sense, but what is the alternative ?
}
}
I want to use this class as a util class for any client who can pass on their json string to the myMethod and get the desired Java object that maps to the class they mention while instantiating MyClass.
So as an example,a client code that does
MyClass<MyType1> o1 = new MyClass<MyType1>();
MyType1 myType1 = o1.myMethod(aJsonString);
get an instance of MyType1
and another code that does
MyClass<MyType2> o2 = new MyClass<MyType2>();
MyType2 myType2 = o2.myMethod(anotherJsonString);
get an instance of MyType2
I'm seeing many posts related to TypeReference and JavaType usage but not able to get hold of exactly how to fit them for my above use case.
My main objective is that MyClass should actually act as a util class without knowing at compile time what is the Java object it will convert the input json string to. It should decide that at runtime only.
Any help will be highly appreciated !
Many thanks and Best Regards
Below solution works for you. Have a private instance of type Class and initialize it in constructor.
import org.codehaus.jackson.map.ObjectMapper;
public class MyClass<T> implements IMyInterface<String, T> {
private Class<T> clazz;
public MyClass(Class<T> clazz) {
this.clazz = clazz;
}
#Override
public T myMethod(String jsonString) throws Exception {
ObjectMapper mapper = new ObjectMapper();
T result = mapper.readValue(jsonString, clazz);
return result;
}
}
You client code will be :
MyClass<MyType1> o1 = new MyClass<MyType1>(MyType1.class);
MyType1 myType1 = o1.myMethod(aJsonString);
ADDED answer for second question :
In spring context file, define your bean as below.
<bean id="simpleMyType1Class" class="com.yourpackage.MyClass">
<constructor-arg>
<!-- this can be any full path to a class -->
<value>com.yourpackage.MyType1</value>
</constructor-arg>
In Java code, get its bean reference as below.
MyClass<MyType> o1 = (MyClass)context.getBean("simpleMyType1Class");

Resolving generic type with java classmate

After failing miserably trying to use TypeTools Resolving generic type information with TypeTools I am attempting to use https://github.com/cowtowncoder/java-classmate instead.
Can someone help me fix this code?
public T fromMap(S map) {
TypeResolver typeResolver = new TypeResolver();
ResolvedType type = typeResolver.resolve((new MapperImpl<T, S>() {}).getClass());
List<ResolvedType> params = type.typeParametersFor(MapperImpl.class);
ResolvedType typeT = params.get(0);
ObjectMapper objectMapper = new ObjectMapper();
T obj = objectMapper.convertValue(map, (Class<T>) typeT.getErasedType());
return obj;
}
I am getting this error:
java.util.LinkedHashMap cannot be cast to LoginInputMapTest$Foo
java.lang.ClassCastException at
shouldMapToFoo(LoginInputMapTest.java:83)
with this minimal test case:
public static class Foo {
private String a;
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
}
#Test
public void shouldMapToFoo() {
Map<String, Object> map = new HashMap<>();
map.put("a", "aaa");
Mapper<Foo, Map<String, Object>> mapper = new MapperImpl<>();
Foo foo = mapper.fromMap(map);
Assert.assertEquals(foo.getA(), map.get("a"));
}
There's nothing you can do within your fromMap method to get the type argument provided that was bound to your type variable T.
I suggest you create a Mapper implementation specifically for Foo.
class FooMapperImpl<S> implements Mapper<Foo, S> {
public Foo fromMap(S map) {
ObjectMapper objectMapper = new ObjectMapper();
Foo obj = objectMapper
.convertValue(map, Foo.class);
return obj;
}
}
(Though I don't see why you need a source type S if it's always going to be a Map.)
It seems to me that you do not fully understand the way Java generic types work, with respect to type variables (T, S). A good place to learn more about this is:
http://www.angelikalanger.com/GenericsFAQ/FAQSections/ParameterizedTypes.html
but basically type variables do not carry any run time generic type information. So while you are nominally calling a method with certain parameterization, nothing happens unless you pass actual Class instance suitable parameterized. So your method compiled to bytecode is little more than:
public Object fromMap(Object map) { ... }
Now, if you pass a Map as map, runtime type will be simply Map.class and there are no type parameters specified: Java values do not have any runtime type parameterization information. Underlying class is the same between, say, Map<String,Number> and Map<UUID,byte[]>. Declarations of parameters only affect Java compiler, which adds necessary casts to ensure that value types get cast properly.
No library can find information that is there, unfortunately. So usage as you suggest is not possible to implement as-is.
This does not mean that you could not pass typing, but it means that it must be passed from outside. With basic Jackson, you have TypeReference you can use:
new TypeReference<Map<KeyType, ValueType>>() { };
would construct reference to type Map<KeyType,ValueType>.
Or you can construct these programmatically using TypeFactory; something like:
mapper.getTypeFactory().constructMapType(Map.class, KeyType.class, ValueType.class);
// or with recursively constructing nested generic types
Now: ClassMate can, conversely, extract type information out of class definitions. If you have class with fields, methods that use generic type declaration, it is difficult to easily find out declared parameterization. But it does not sound like this is what you actually want or need here. Rather you should be able to build it using Jackson's type handling functionality.

java jackson parse object containing a generic type object

i have the following problem.
I have to parse a json request into an object that contains a generic type field.
EDIT
i have made some tests using a regular class type (so i make it work before i replace it with generic). Now parsing for a single element works great.
The issue is when i need to parse out a list object out of that class.
So i have to inform jackson somehow that my T is of type list instead of just AlbumModel.
Here is what i have tried.
#Override
public ListResponseModel<AlbumModel> parse(String responseBody) throws Exception {
JavaType type = mapper.getTypeFactory().constructParametricType(ResponseModel.class,
AlbumModel.class);
return mapper.readValue(responseBody,
mapper.getTypeFactory().constructParametricType(ResponseModel.class, type));
}
But the code above doesn't work. what is the solution for something like this?
my generic type in the ListResponseModel is defined like: List<T> data
succeeded like:
public class BaseResponseModel<T> {
#JsonProperty("data")
private T data;
#JsonProperty("paginations")
private PaginationModel pagination;
}
so far i have the following code but it always parses into a Hash.
public class ResponseParser extends BaseJacksonMapperResponseParser<ResponseModel<AlbumModel>> {
public static final String TAG = ResponseParser.class.getSimpleName();
#Override
public ResponseModel<AlbumModel> parse(String responseBody) throws Exception {
return mapper.readValue(responseBody,
mapper.getTypeFactory().constructParametricType(ResponseModel.class, AlbumModel.class));
}
}
public abstract class BaseJacksonMapperResponseParser<T> implements HttpResponseParser<T> {
public static final String TAG = BaseJacksonMapperResponseParser.class.getSimpleName();
public static ObjectMapper mapper = new ObjectMapper();
static {
mapper.disable(Feature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper.enable(Feature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
mapper.configure(SerializationConfig.Feature.WRAP_ROOT_VALUE, true);
}
}
I agree with eugen's answer but just wanted to expand on it a bit. The first step is to refactor your parse method so it takes a second argument. Instead of allocating the type reference in your method, you require the caller to pass in a TypeReference instance.
public BaseResponseModel<T> parse(String responseBody, TypeReference<T> ref) throws Exception {
return mapper.readValue(responseBody, ref);
}
Unfortunately your snippet does not show the code which calls parse - so I'll make something up:
BaseResponseParser<Collection<Person>> parser = new BaseResponseParser<Collection<Person>>();
BaseResponseModel<Collection<Person>> result = parser.parse(jsonText, new TypeReference<Collection<Person>>(){});
Notice that when the TypeReference instance is compiled in this case, it a type reference to the real concrete class that we expect.
You could do the same thing passing in a Class at runtime, however TypeReference is a bit more powerful because it even works when type T is a generic collection. There is some magic in the TypeReference implementation that allows it to hold onto type information that would normally be erased.
[update]
Updated to use Collection<Person>. Note - as far as I know as List<Whatever> should work also, but I double checked a project where I was using jackson to deserialize collections. Base class Collection definitely worked so I stayed with that.
Your type T will be "erased" at runtime, so Jackson does not know what is the real type of T and deserializes it to a Map. You need a second parameter to your parse method that will be Class<T> clazz or TypeReference<T> or java.lang.reflect.Type.
EDIT
Small explanation on the magic of TypeReference. When you do new XX() {} you are creating a anonymous class, so if it is a class with typevariables (parameterized if you prefer), new X<List<Y>>() {}, you will be able to retrieve List<Y> as a java Type at runtime. It is very similar as if you had done :
abstract class MyGenericClass<T> {}
class MySpecializedClass extends MyGenericClass<List<Y>> {}
Since you're using Jackson you probably need to create a custom JsonDeserializer or JsonSerializer depending on whether you're handing the response or request. I've done this with Dates because on my response I want a standard view. I'm not 100% positive it will work with a generic field though. Here is an example of what I'm doing:
public class DateSerializer extends JsonSerializer<Date> {
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZ");
#Override
public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
String dateString = dateFormat.format(value);
jgen.writeString(dateString);
}
}
Then I just add it to my class like so:
#JsonSerialize(using = DateSerializer.class)
public Date getModifiedDate() {
return modifiedDate;
}

GSON deserialization : how to know the objects?

I try to use gson library to deserialize a flow of objects sent to me.
In all examples i've seen, when the method fromJson is called, we already know what type of object we expect to have.
In my case, I receive a flow of different objects and i'd like to know the best way to know the classes of objects before deserialize them.
{ A : {...}, B : { B1 : {...}, B2 : {...} }, C : {...} }
In this example, I'd like to have a way to know that 3 objects have been sent to me : A.class, B.class and C.class
Thanks
The documentation contains examples of deserializations using arbitrary classes or in two passes (first general deserialization in a collection, then content deserialization).
This exemple looks exactly like what you need. You could adapt it to use
JsonObject obj = parser.parse(json).getAsJsonObject();
to get a JsonObject instead of an array so that you can iterate on all properties (using entrySet) and deserialize according to the names (a = gson.fromJson(myjsonelement, A.class);) by simply mapping names to classes.
Yeah i too stumbled upon this issue. There is no way gson can figure out actual class of a field value. It simply tries to instantiate class used to define the field. Needless to say it is often not what we want.
so if you had, say
class C {
private A a;
private A c;
}
class B extends A {
}
then at runtime you
C c;
c.a = new B();
c.c = new B();
after deserialisation what you get is
c.a.getClass()==A.class;
c.b.getClass()==A.class;
so you would have to specify the subclass explicitly. Here is a wrapper class that is gson friendly.
public class S<T> {
private String objectClass;
private String rawObjectRepresentation;
// Gson needs no args constructor
public S() {
}
public S(T obj) {
objectClass = obj.getClass().getName();
rawObjectRepresentation = getGson().toJson(obj);
}
#SuppressWarnings("unchecked")
public T extract() throws ClassNotFoundException {
final Class<?> clazz = Class.forName(objectClass);
return (T)getGson().fromJson(rawObjectRepresentation, clazz);
}
private Gson getGson() {
return new GsonBuilder().create();
}
#Override
public String toString() {
return "type:"+objectClass;
}
}
If there is a field on the json object that you can use to identify the subclass you need to use, then you can use Gson on Fire: https://github.com/julman99/gson-fire
It has a feature called Type Selector that does exactly what you need.
Imagine a Base class and two child classes, A and B, then the code would look like this:
GsonFireBuilder builder = new GsonFireBuilder()
.registerTypeSelector(Base.class, new TypeSelector<Base>() {
#Override
public Class<? extends Base> getClassForElement(JsonElement readElement) {
String kind = readElement.getAsJsonObject().get("kind").getAsString();
if(kind.equals("a")){
return A.class; //This will cause Gson to deserialize the json mapping to A
} else if(kind.equals("b")) {
return B.class; //This will cause Gson to deserialize the json mapping to B
} else {
return null; //returning null will trigger Gson's default behavior
}
}
});
Gson gson = builder.createGson();

Categories