Jackson JSON User Group

JSON in Java the Right Way -- Action, Jackson!

Mapper not include type information when serializing object. Why?

@JsonSubTypes({@JsonSubTypes.Type(value=D.class)})
@JsonTypeInfo(use = Id.NAME, property = "typeId")
class B {}
class D extends B {}

public static void main(String[] args) throws Exception {

  List<B> list = new ArrayList<>();

  list.add(new B());

  String json = new ObjectMapper().writeValueAsString(list); //json not contain field "typeId", deserialization impossible (((

  list = new ObjectMapper().readValue(json, new TypeReference<List<B>>(){}); //...JsonMappingException: Unexpected token (END_OBJECT), expected FIELD_NAME: missing property 'typeId'...
}

Views: 2771

Reply to This

Replies to This Discussion

This is probably due to type erasure: type of List is seen as 'List<?>', roughly equivalent to 'List<Object>', and since Object.class is considered static type, no type information is included.

What is needed is to either sub-class List (like 'public class MyList extends List<B>'), which works, but is ugly if you have lots of different kinds of lists, or forcing of serialization type. Latter can be done by constructing ObjectWriter with something like:

  ObjectWriter w = mapper.writerWithType(new TypeReference<List<B>>() {});
  String json = w.writeValueAsString(list);

and this should then contain type information as expected, now that type is explicitly passed, instead of Jackson using 'value.getClass()' (which is type-erased for List instances).

Thanks. But we don't need type information in this case.

Our type is: list.get(0).getClass()

And then scan up to hierarchy @JsonSubTypes and @JsonTypeInfo for correct writing type info for every object in list.

And resulting json is: [{"typeId":"D",...}, {"typeId":"B",...}, {"typeId":"Sub-class of B",...}]


Unfortunately that is not how type information works: it MUST use declared type, to ensure that same type information settings are used; otherwise information could depend on per-element basis, and this distinction is impossible to figure out during deserialization.

So there is difference between serialization of data (which does use actual runtime type), and serialization of type information (which must use declared type). That is why you need to provide static type for this particular case.

Hope this helps.

Im my case if I convert List to Array all works ok:

writeValueAsString(list.toArray(EMPTY_ARRAY))

Types are included in json string.

Yes, because Java arrays retain full type information, whereas collections do not. That is due to Java Type erasure. So using arrays is often safer.

RSS

© 2014   Created by Tatu Saloranta.   Powered by

Badges  |  Report an Issue  |  Terms of Service