How to correctly serialize/deserialize an enum with custom type information

12,162 views
Skip to first unread message

david_m...@premierinc.com

unread,
Jan 14, 2016, 6:39:00 PM1/14/16
to jackson-user
Hello all,

I'm hoping someone can give me some insight into what I'm doing wrong here.

I have an enum type, and I want to serialize it including the class information using the @JsonTypeInfo annotation, so that it can be deserialized into the correct class automatically:

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property="@class")
public enum SimpleFakeRecordType {
    FAKE1("Fake1"),
    FAKE2("Fake2"),
    FAKE3("Fake3");

    @JsonProperty("name")
    private String name;

    SimpleFakeRecordType(final String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

When I do this, it seems to correctly serialize an object, for example:
SimpleFakeRecordType.FAKE1 becomes {"@class":"test.SimpleFakeRecordType","name":"Fake1"}

However, deserializing this same value causes an exception:
com.fasterxml.jackson.databind.JsonMappingException: 
Unexpected token (START_OBJECT), expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information for class test.SimpleFakeRecordType 
at [Source: {"@class":"test.SimpleFakeRecordType","name":"Fake1"}; line: 1, column: 1] 
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148) 
at com.fasterxml.jackson.databind.DeserializationContext.wrongTokenException(DeserializationContext.java:946) 
at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._locateTypeId(AsArrayTypeDeserializer.java:127) 
at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:93) 
at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer.deserializeTypedFromScalar(AsArrayTypeDeserializer.java:63) 
at com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer.deserializeWithType(StdScalarDeserializer.java:26) 
at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:42) 
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3736) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2726) 
at test.SimpleFakeRecordTypeSerializationTest$anonfun$1.apply$mcV$sp(SimpleFakeRecordTypeSerializationTest.scala:20) 

Test case is available here, and a simple maven project that runs the test is here.

Any help or insights into what I am doing wrong to get the roundtrip serialization working correctly would be much appreciated.

Thanks,
David


Tatu Saloranta

unread,
Jan 14, 2016, 6:44:42 PM1/14/16
to jackso...@googlegroups.com
It is probably not possible to deserialize Enums from JSON Objects at this point. Support for Objects as serialization format was only meant for output side.

But if this was to work, it would have to be using @JsonCreator annotation on a factory method (instances will not work as code can not invoke Enum constructors, for good reason). If so, it should be acceptable to take in any kind of delegate type; Map, JsonNode, even some POJO type. And then you would have code in there to figure out which Enum value to return.

If @JsonCreator approach does not work (quite possible? don't think I've tested it), please file an issue for `jackson-databind`: I think it is reasonable to expected that to work for any type.

-+ 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...@googlegroups.com.
To post to this group, send email to jackso...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

david_m...@premierinc.com

unread,
Jan 14, 2016, 7:03:24 PM1/14/16
to jackson-user
Tatu, thanks for the quick reply.  I've added the following method to the enum:

    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
    public static SimpleFakeRecordType create(@JsonProperty("name") String name) {
        return SimpleFakeRecordType.valueOf(name);
    }

Doing so changes the error to:
com.fasterxml.jackson.databind.JsonMappingException:
Can not construct instance of test.SimpleFakeRecordType, problem: Name is null
 at [Source: {"@class":"test.SimpleFakeRecordType","Name":"Fake1"}; line: 1, column: 1]
        at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:152)
        at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:887)
        at com.fasterxml.jackson.databind.deser.std.EnumDeserializer$FactoryBasedDeserializer.deserialize(EnumDeserializer.java:236)
        at com.fasterxml.jackson.databind.deser.std.EnumDeserializer$FactoryBasedDeserializer.deserializeWithType(EnumDeserializer.java:243)
        at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:42)
        at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3736)
        at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2726)
        at test.SimpleFakeRecordTypeSerializationTest$$anonfun$1.apply$mcV$sp(SimpleFakeRecordTypeSerializationTest.scala:20)

Did you mean that there should also be factory methods that take Map and JsonNode annotated with @JsonCreator?  I am guessing this means it is not set up to correctly deserialize custom enums.  I'm going to experiment with a custom deserializer and will post an issue in jackson-databind as you requested.

Thanks,
David 

Tatu Saloranta

unread,
Jan 14, 2016, 7:15:21 PM1/14/16
to jackso...@googlegroups.com
On Thu, Jan 14, 2016 at 4:03 PM, <david_m...@premierinc.com> wrote:
Tatu, thanks for the quick reply.  I've added the following method to the enum:

    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
    public static SimpleFakeRecordType create(@JsonProperty("name") String name) {
        return SimpleFakeRecordType.valueOf(name);
    }


Ah. Forgot to mention that probably only delegating variety (one with NO @JsonProperty) would work at this point. So you would define instead

    @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
    public static SimpleFakeRecordType create(JsonNode json) {
        return SimpleFakeRecordType.valueOf(json.get("name"));
    }

That is not to say that use of properties-based one should not work, ideally.
But that I think that if anything does work, it'll be delegating one.


-+ Tatu +-

david_m...@premierinc.com

unread,
Jan 15, 2016, 1:43:40 PM1/15/16
to jackson-user
Ok, great news -- this does work using JsonCreator.Mode.DELEGATING.  Thanks for the help getting that figured out.
As noted, it does not work correctly for JsonCreator.Mode.PROPERTIES so I've updated my examples and test case (https://github.com/dmcwhorter/jackson-custom-enum-test) to show DELEGATING working but PROPERTIES breaking, and created an issue in jackson-databind to add support for PROPERTIES (https://github.com/FasterXML/jackson-databind/issues/1082).

Tatu Saloranta

unread,
Jan 15, 2016, 7:53:14 PM1/15/16
to jackso...@googlegroups.com
Thank you!

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