从 2.10 升级时 Jackson 序列化失败(InvalidDefinitionException:未为类型 java.lang.Object 实现类型 ID 处理)

Jackson serialization failing when upgrading from 2.10 (InvalidDefinitionException: Type id handling not implemented for type java.lang.Object)

我正在从 Jackson 2.10 升级到 2.12,突然这个简单的测试(之前运行良好)现在失败了:

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

我从 and this one 等其他类似帖子中了解到,Jackson 可能难以 反序列化 多态类型,但这在 序列化 不是反序列化。此外,当我尝试创建自己的 Exception subclass 并尝试对其进行序列化时,它工作得很好。我正在尝试将其用作通用序列化程序,因此我不想为每种对象类型手动添加自定义序列化程序——我什至不知道为什么 IllegalFieldValueException 似乎是唯一的 class 无法序列化。所以我有两个主要问题:

  1. 为什么我从 Jackson 2.10 升级到更高版本时突然失败?我没有改变任何其他东西!是否有我可以使用的配置选项来复制早期版本的行为?
  2. 为什么 IllegalFieldValueException 是唯一似乎无法序列化的类型?当我尝试序列化其他异常 subclasses 或多态类型时,我没有看到此错误。这个 class 有什么特别之处? (还有其他 class 可能导致相同行为的问题吗?)

简而言之,Jackson 通过这次提交基本上打破了这种行为:https://github.com/FasterXML/jackson-databind/commit/85c9c8544f0c4f01e88241acc1573746df4f755d

具有讽刺意味的是,实际上这里有一位开发人员 (tatu) 的评论,询问他们是否应该添加覆盖选项允许 force-POJO 序列化:https://github.com/FasterXML/jackson-databind/blob/2.14/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java#L891

不幸的是,tatu 的这条评论显然被忽略了,因为没有办法覆盖或禁用此检查。即使在最新版本的 Jackson 中,它仍然有同样的不良行为。

好消息是,正如您在 Jackson 映射器的 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 实现中看到的那样,JodaModule 实际上并不包含异常类型,因此您仍然会遇到同样的错误。

从长远来看,Jackson 的理想解决方案是添加一个可配置的序列化选项以强制对 time-related 类型进行 POJO 序列化,and/or 更新 JodaModule 以包含异常类型。但是现在,您可以通过创建 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);
    }
}

然后将您的 ObjectMapper 设置为使用此工厂:

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!