FasterXML / jackson-dataformats-binary

Uber-project for standard Jackson binary format backends: avro, cbor, ion, protobuf, smile
Apache License 2.0
310 stars 133 forks source link

Round-trip Ion serialization/deserialization breaks when @JsonTypeInfo annotation is present #225

Closed pymaxion-amzn closed 3 years ago

pymaxion-amzn commented 3 years ago

I have not done too much investigation into this problem, but it appears that the @JsonTypeInfo annotation might not be handled properly by the IonObjectMapper code path. I discovered this issue while trying to upgrade my Jackson dependencies from version 2.8.x to 2.10.x, which involved switching from an old, now-deprecated JacksonIon library onto this one.

Let's say that I have the following classes, which approximately model my real-world use case:

@JsonTypeInfo(use = Id.CLASS, include = As.PROPERTY, property = "@class")
public abstract class BaseClass { // empty }

@Data
public class Subclass extends BaseClass {
    private final String customField1;
    private final int customField2;
}

I would expect the following test to succeed, but it sadly does not:

@Test
  void subclass_RoundTripSerialization_IsSuccessful() throws IOException {
    IonObjectMapper ionObjectMapper = new IonObjectMapper();
    Subclass instance = new Subclass("some value", 42);
    IonValue instanceAsIon = ionObjectMapper.writeValueAsIonValue(instance);
    BaseClass roundTripInstance = ionObjectMapper.readValue(instanceAsIon, BaseClass.class);

    assertTrue(roundTripInstance instanceof Subclass);
    assertEquals(instance, roundTripInstance);
  }

This test succeeds with the older JacksonIon library, but does not succeed now. Specifically, the test throws an exception when executing ionValueMapper.parse(instanceAsIon, BaseClass.class), something like:

com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Missing type id when trying to resolve subtype of [...]: missing type id property '@class'

Indeed, when looking at the serialized data, it does not match what I expect based on what I provided in the @JsonTypeInfo annotation:

// expected
{
  '@class':"my.package.Subclass",
  customField1:"some value",
  customField2:42
}

// actual
'my.package.Subclass'::{
  customField1:"some value",
  customField2:42
}

The exception message is accurate in that the serialized version does not have an @class type id property; the type id information is instead written as an Ion annotation.

Note that although this example involves polymorphism, I wasn't able to get the test to succeed even with non-polymorphic types, for example:

@JsonTypeInfo(use = Id.CLASS, include = As.PROPERTY, property = "@class")
@Data
public class SimpleObj {
  private final int field;
}

I get the same exception as before when performing round-trip serialization and deserialization with IonObjectMapper.

It seems as though the serialization logic is ignoring the @JsonTypeInfo annotation and always choosing to write the type info in an Ion annotation, while the deserialization logic is reading the annotation and therefore expects the type info in a property named @class, and it throws an exception when no such property is found.

Moreover, for reasons relating to backwards compatibility, I would really like to serialize an instance of Subclass with the type info in an @class property rather than an Ion annotation, but after poking around in the source code, it seems the decision to write type ids as Ion annotations is more-or-less hard-coded:

Just to unblock myself, I've been playing around with various workarounds, including extending IonGenerator and supplying it to IonObjectMapper via my own custom IonFactory, as well as changing the behavior of the methods I linked above based on a new configuration option which can be set on the IonObjectMapper. However, I'm not completely sure why @JsonTypeInfo is being ignored during serialization in the first place, and don't know enough about the internals of Jackson at the moment to investigate effectively.

This is potentially related to this old pull request: https://github.com/FasterXML/jackson-dataformats-binary/pull/109. I don't know how much overlap there might be between these two issues.

pymaxion-amzn commented 3 years ago

Looks like this issue is related to (or maybe duplicates) an older, still-open issue: [Ion] Polymorphic deserialization does not work correctly #149

toddjonker commented 3 years ago

Hasn't this been resolved by PR #232 ?

pymaxion-amzn commented 3 years ago

Yes, this was resolved by PR #232. I'll close the issue now, thanks for the reminder!