Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JsonTypeInfo is ignored when serializing collections #281

Closed
PavelPenkov opened this issue Aug 31, 2016 · 3 comments
Closed

JsonTypeInfo is ignored when serializing collections #281

PavelPenkov opened this issue Aug 31, 2016 · 3 comments

Comments

@PavelPenkov
Copy link

It seems that @JsonTypeInfo annotation is only used when serializing arrays of objects and ignored for other types of collections. Tested on 2.8.2

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
case class Foo(id: Int)

object Hello extends App {
  val mapper = new ObjectMapper() with ScalaObjectMapper
  mapper.registerModule(DefaultScalaModule)

  val foo = Foo(42)

  println(mapper.writeValueAsString(foo))  // => {"@class":"Foo","id":42} - correct
  println(mapper.writeValueAsString(Array(foo))) // => [{"@class":"Foo","id":42}] - correct
  println(mapper.writeValueAsString(List(foo))) // =>  [{"id":42}] - incorrect
  println(mapper.writeValueAsString(Map("foo" -> foo))) // => {"foo":{"id":42}} - incorrect
}
@cowtowncoder
Copy link
Member

@PavelPenkov Not really; Collections and arrays both include type information, but Java Type Erasure only affects former: Lists/Collections (etc) are generic, whereas arrays are not generic.

Problem specifically is that when you pass List (etc) value directly, there is no generic type information available from value; type is equivalent to List<?>. But arrays are strongly typed (Foo[]).
Since type id handling requires static type information for collection elements, you see behavior as above. This is unfortunately something that we can not do much about.

There are various work-arounds;

  1. Do not use generic types as root values: use a wrapper POJO as root value (generic types are fine beyond the root value itself)

  2. Create non-generic subtype (in Java it'd be public class FooList extends ArrayList<Foo> { }), runtime instances of which now do have enough type information to allow correct handling

  3. Specify JavaType or TypeReference when serializing; something like:

    String json = mapper.writerFor(new TypeReference<List>() { }).writeValueAsString(....)

in which case type information is passed explicitly and incomplete runtime type information ignored.

Hope this helps!

@nbauernfeind
Copy link
Member

@cowtowncoder I thought JsonTypeInfo could be found by walking up the type heirarchy of the object being serialized even when type information was not available. Why does it matter what "known" types (i.e. using type references or non-gen subtypes) there are on the collection?

@cowtowncoder
Copy link
Member

@nbauernfeind No, type info handling must be based on statically available information to guarantee (well, try to anyway) deserialization can use same base definition. For performance reasons it would also be prohibitive to dig into these details on element-by-element basis; but fundamentally collections are considered to be homogenous with respect to "base type", which is what polymorphic type handling uses.

So, for example, for property like:

public class Foo {
public List stuff;
}

only @JsonTypeInfo in Base (or its supertypes) is considered for determining how type ids for elements are handled. It does not matter which Base subtypes elements for this purpose.
If Base (or supertypes) does not have @JsonTypeInfo, polymorphic type handling is not enabled.

Actual content serialization is dynamic, however, so all properties of elements are serialized; and type id itself of course relies on actual type during serialization.

From this, then, root value handling differs in that instead of finding List<Base> from class definition, and having access to generic definition, code has to use value.getClass(), which can only give type-erased definition.

This question is a FAQ and I really should try to formulate it in an easily referenceable form; it is something that will not be changed at this point, and has been the behavior since polymorphic handling was implemented (in Jackson 1.5 I think).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants