@JsonTypeInfo doesn't serialize property value in a List

3,229 views
Skip to first unread message

krrrr38

unread,
Oct 6, 2016, 11:57:18 PM10/6/16
to jackson-user
Hi. I use jackson-databind 2.8.3.

I want to serialize value with `@JsonTypeName`. When serialize with `List`, the property value is not shown.

public class Main {
   public static void main(String[] args) throws Exception {
       ObjectMapper om = new ObjectMapper();
       System.out.println(om.writeValueAsString(new ExampleContent()));
       System.out.println(om.writeValueAsString(Arrays.asList(new ExampleContent())));
   }

    @JsonTypeInfo(
           use = JsonTypeInfo.Id.NAME,
           include = JsonTypeInfo.As.PROPERTY,
           property = "type",
           visible = true
   )
   @JsonSubTypes({
           @JsonSubTypes.Type(ExampleContent.class)
   })
   abstract static public class AbstractContent {
   }

    @JsonTypeName("text")
   public static class ExampleContent extends AbstractContent {
   }
}


result

{"type":"text"}
[{}]


expected?

{"type":"text"}
[{"type":"text"}]

Do I need to add more annotation or something?

Regargs.

Tatu Saloranta

unread,
Oct 7, 2016, 1:52:18 PM10/7/16
to jackson-user
So, what happens is that while you think you are giving a List<ExampleContent> (or List<AbstractContent>), what runtimes sees is a List<? extends Object>. Since `Object` has no `@JsonTypeInfo`, no type is included.

You have been tripped by Java Type Erasure. Also, my general suggestion is to NEVER EVER use generic type as the root type, but always use a non-generic type. There are multiple ways to do that. Like:

1. Always use a POJO to contain Lists, Maps (wrapper)
2. Sub-class Lists, Maps:

    public class ExampleList extends ArrayList<AbstractContent> { }

   because this does retain type information

But there is also one way to make generic root type to work (although with caveats)

1. Use `ObjectWriter`, specifying actual type, without erasure:

   mapper.writerFor(new TypeReference<List<AbstractContent>>() { }).writeValueAsString(list);

in this case you may hit other problems, since type is now fixed for content as well, so this may fail for some type definitions.

-+Tatu +-


--
You received this message because you are subscribed to the Google Groups "jackson-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jackson-user+unsubscribe@googlegroups.com.
To post to this group, send email to jackso...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

krrrr38

unread,
Oct 7, 2016, 5:44:52 PM10/7/16
to jackson-user
I see.

I could get what I want with such ways.

Thanks for quick response.

2016年10月8日土曜日 2時52分18秒 UTC+9 Tatu Saloranta:
To unsubscribe from this group and stop receiving emails from it, send an email to jackson-user...@googlegroups.com.

Tatu Saloranta

unread,
Oct 7, 2016, 6:31:59 PM10/7/16
to jackso...@googlegroups.com
No problem; this is a common problem users hit, and unexpected one.
It is sort of a FAQ, just not sure how to write concise entry.

Note, however, that this problem does NOT happen with generic types referenced as Java class properties -- it is strictly limited to cases where the value is handed to Jackson, and there is no declaration (Method, Field, constructor parameter) to give proper generic type information. So workaround is only needed at this level and nowhere else.

-+ Tatu +-


To unsubscribe from this group and stop receiving emails from it, send an email to jackson-user+unsubscribe@googlegroups.com.

Rob Sargent

unread,
May 12, 2017, 7:28:17 PM5/12/17
to jackson-user
I'm generating classes with these annotations 
 @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="class") 
 @JsonTypeName("persist.seg.tables.pojos.Segment")
and I generate json such as 
{
  "userName" : "rob",
  "segmentList" : [ {
    "class" : "persist.seg.tables.pojos.Segment",
    "id" : "d4fca0dc-c20f-436d-b2d5-45950c148357",
    "chrom" : 1,
    "markersetId" : "668ea0fd-e028-408f-bc46-58691aaf194e",
    "probandsetId" : "48f7be44-87b7-4aec-8a8f-6eff0ff21da2",
    "startbase" : 1123456,
    "endbase" : 1223456,
    "eventsGreater" : 299000,
    "eventsEqual" : 300,
    "eventsLess" : 700
  }, {
    "class" : "persist.seg.tables.pojos.Segment",
    "id" : "52a8116d-8f68-4950-8641-730b4d99b34c",
    "chrom" : 1,
    "markersetId" : "668ea0fd-e028-408f-bc46-58691aaf194e",
    "probandsetId" : "48f7be44-87b7-4aec-8a8f-6eff0ff21da2",
    "startbase" : 1123456,
    "endbase" : 1223456,
    "eventsGreater" : 299900,
    "eventsEqual" : 30,
    "eventsLess" : 70
  }, {
    "class" : "persist.seg.tables.pojos.Segment",
    "id" : "c021517a-5b3e-48b8-928c-7790c0fe9a73",
    "chrom" : 1,
    "markersetId" : "668ea0fd-e028-408f-bc46-58691aaf194e",
    "probandsetId" : "48f7be44-87b7-4aec-8a8f-6eff0ff21da2",
    "startbase" : 1123456,
    "endbase" : 1223456,
    "eventsGreater" : 299990,
    "eventsEqual" : 3,
    "eventsLess" : 7
  } ]
}
But without a no-argument constructor the deserialization fails.  I thought the annotations would answer the suggestion in the error message "perhaps need to add/enable type information?"

Tatu Saloranta

unread,
May 12, 2017, 7:57:12 PM5/12/17
to jackson-user
I am not sure I understand the question wrt requiring no-arguments
constructor: yes, any object that Jackson is to create must be
constructed somehow: and choice is usually either via zero-arguments
constructor, or a constructor with `@JsonCreator` annotation.

What exactly is the exception you get? And what does the class being
deserialized look like wrt consructors it has?

-+ Tatu +-

Rob Sargent

unread,
May 15, 2017, 11:52:27 AM5/15/17
to jackson-user
I missed the need for @JsonCreator. I'll try that now. Does having a no-argument constructor remove the need for any of the annotations I'm using (JsonTypeInfo, JsonTypeName)

Here's the full error message/hint:
Can not construct instance of persist.seg.tables.pojos.Segment: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)
 at [Source: payload.json; line: 5, column: 5] (through reference chain: edu.utah.camplab.app.SGSPayload["segmentList"]->java.util.ArrayList[0])

Tatu Saloranta

unread,
May 22, 2017, 3:25:12 PM5/22/17
to jackson-user
On Mon, May 15, 2017 at 8:52 AM, Rob Sargent <robjs...@gmail.com> wrote:
> I missed the need for @JsonCreator. I'll try that now. Does having a
> no-argument constructor remove the need for any of the annotations I'm using
> (JsonTypeInfo, JsonTypeName)

It depends on exactly what you are trying to do (only serializing? No
need for that;
deserializing, may still need _iff_ heterogenous contents).
But if contents are homogenous (same value class) for deserialization,
there are simpler ways to achieve this than forcing inclusion of
(polymorphic) type information.

>
> Here's the full error message/hint:
> Can not construct instance of persist.seg.tables.pojos.Segment: no suitable
> constructor found, can not deserialize from Object value (missing default
> constructor or creator, or perhaps need to add/enable type information?)
> at [Source: payload.json; line: 5, column: 5] (through reference chain:
> edu.utah.camplab.app.SGSPayload["segmentList"]->java.util.ArrayList[0])

Right, so you are trying to deserialize, and without specifying actual
content type.

I think what is needed here is piece of code you use to invoke
deserialization; that would show what is needed beyond constructor. I
don't think that is the only problem here...
Actually, also, would need to see result type (`SGSPayload`), possibly
in simplified form.

-+ Tatu +-
Reply all
Reply to author
Forward
0 new messages