2

I'm upgrading from Jackson 2.10 to 2.12, and suddenly this simple test (which was working fine before) is now failing:

ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping(DefaultTyping.NON_FINAL);
mapper.valueToTree(new org.joda.time.IllegalFieldValueException("testName", "testValue")); // causes error
java.lang.IllegalArgumentException: Type id handling not implemented for type java.lang.Object (by serializer of type com.fasterxml.jackson.databind.ser.impl.UnsupportedTypeSerializer)
    at com.fasterxml.jackson.databind.ObjectMapper.valueToTree(ObjectMapper.java:3312)
    at com.amazon.ets.util.exception.ExceptionSerializationTest.shouldSerializeException(ExceptionSerializationTest.java:77)
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Type id handling not implemented for type java.lang.Object (by serializer of type com.fasterxml.jackson.databind.ser.impl.UnsupportedTypeSerializer)
    at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77)
    at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1276)
    at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:400)
    at com.fasterxml.jackson.databind.JsonSerializer.serializeWithType(JsonSerializer.java:160)
    at com.fasterxml.jackson.databind.ser.impl.TypeWrappedSerializer.serialize(TypeWrappedSerializer.java:32)
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480)
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319)
    at com.fasterxml.jackson.databind.ObjectMapper.writeValue(ObjectMapper.java:3126)
    at com.fasterxml.jackson.databind.ObjectMapper.valueToTree(ObjectMapper.java:3307)
    ... 24 more

I've gleaned from other similar posts like this one and this one that Jackson can struggle deserializing polymorphic types, but this is erroring on serialization not deserialization. Additionally, when I just try creating my own Exception subclass and try serializing it, it works just fine. I'm trying to use this as a general purpose serializer, so I don't want to have to manually add custom serializers for every object type -- I don't even know why IllegalFieldValueException in particular seems to be the only class that fails to serialize. So I have two main questions:

  1. Why is this suddenly failing when I upgrade from Jackson 2.10 to a later version? I didn't change anything else! Is there a configuration option I can use to have it replicate the earlier version's behavior?
  2. Why is IllegalFieldValueException the only type that seems to be failing to serialize? When I try serializing other exception subclasses or polymorphic types I don't see this error. What's so special about this particular class? (And are there any other classes that might cause the same behavior?)
Dasmowenator
  • 5,505
  • 5
  • 36
  • 50
  • It's also worth noting that `mapper.canSerialize(IllegalFieldValueException.class)` returns `true` – Dasmowenator May 02 '22 at 23:58
  • I am also facing a similar issue, if you have found an answer can you please provide it? I have posted my question: https://stackoverflow.com/q/72089765/7584240, if you get a chance can you please have a look and provide some suggestions? – BATMAN_2008 May 03 '22 at 09:08

1 Answers1

2

The short answer is that Jackson basically broke this behavior with this commit: https://github.com/FasterXML/jackson-databind/commit/85c9c8544f0c4f01e88241acc1573746df4f755d

Ironically, there's actually a comment from one of the developers here (tatu) who asks whether they should add an override option allow force-POJO serialization: https://github.com/FasterXML/jackson-databind/blob/2.14/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java#L891

Unfortunately, this comment from tatu was apparently disregarded, because there's no way to override or disable this check. Even in the latest version of Jackson, it still has this same bad behavior.

The good news is that, as you can see in the implementation of the checkUnsupportedType() method, it only throws this error when attempting to serializing classes under the java.time or org.joda.time packages. That means you don't need to worry about getting this exception thrown when trying to serialize anything else. The bad news is that even if you add the JodaModule to your Jackson mapper, the JodaModule doesn't actually include exception types, so you'll still get this same error.

In the long term, the ideal fix would be for Jackson to add a configurable serialization option to force POJO serialization for time-related types, and/or for the JodaModule to be updated to include the exception types. But for now, you can fix this behavior by creating a subclass of the BeanSerializerFactory:

public class CustomBeanSerializerFactory extends BeanSerializerFactory {
    public CustomBeanSerializerFactory(SerializerFactoryConfig config) {
        super(config);
    }
    @Override
    protected JsonSerializer<?> _findUnsupportedTypeSerializer(SerializerProvider ctxt, JavaType type, BeanDescription beanDesc) throws JsonMappingException {
        return null;
    }
    @Override
    public SerializerFactory withConfig(SerializerFactoryConfig config) {
        if (_factoryConfig == config) return this;
        return new CustomBeanSerializerFactory(config);
    }
}

Then set your ObjectMapper to use this factory instead:

ObjectMapper mapper = new ObjectMapper();
mapper.setSerializerFactory(new CustomBeanSerializerFactory(null));
mapper.enableDefaultTyping(DefaultTyping.NON_FINAL);
mapper.valueToTree(new org.joda.time.IllegalFieldValueException("testName", "testValue")); // no error anymore - yay!
Dasmowenator
  • 5,505
  • 5
  • 36
  • 50