You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Jackson在缺失反序列化属性时的多态反序列化?

Solution for Jackson Polymorphic Deserialization with Missing Type Property

When dealing with polymorphic deserialization in Jackson where the type identifier (like schemaVersion in your case) might be missing from the input JSON, here are two reliable approaches to fix the issue:

The easiest way to handle missing type properties is to specify a default implementation class directly in the @JsonTypeInfo annotation. This tells Jackson which subclass to use when the type property is absent or unrecognized.

Modify your SuperClass annotation setup like this:

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({ "superClassProperty", "schemaVersion" })
@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME, 
    property = "schemaVersion",
    defaultImpl = SubClassV1.class // Add this line to define fallback type
)
@JsonSubTypes({ 
    @JsonSubTypes.Type(value = SubClassV1.class, name = "1.0"), 
    @JsonSubTypes.Type(value = SubClassV2.class, name = "2.0") 
})
public class SuperClass {
    @JsonProperty("superClassProperty")
    private String superClassProperty;
    
    @JsonProperty("schemaVersion")
    private String schemaVersion = "1.0"; // Default value for serialization
    
    // Add getters, setters, and constructors as needed
}

How it works:

  • If the input JSON includes schemaVersion: "1.0" or "2.0", Jackson will deserialize to the corresponding subclass as expected.
  • If schemaVersion is missing from the JSON, Jackson automatically falls back to SubClassV1 (matching your default value of "1.0").
  • The default schemaVersion value in SuperClass will be retained in the deserialized object unless explicitly overridden by the subclass.

Approach 2: Custom TypeDeserializer (For Advanced Control)

If you need more flexibility (e.g., inferring the type from other properties when schemaVersion is missing), you can create a custom TypeDeserializer to handle the logic manually.

Step 1: Create the Custom Type Deserializer

public class CustomSuperClassTypeDeserializer extends StdTypeDeserializer<SuperClass> {
    private static final long serialVersionUID = 1L;
    private final TypeDeserializer defaultDeserializer;

    public CustomSuperClassTypeDeserializer(TypeDeserializer defaultDeserializer) {
        super(SuperClass.class);
        this.defaultDeserializer = defaultDeserializer;
    }

    @Override
    public SuperClass deserializeTypedFromObject(JsonParser p, DeserializationContext ctxt) throws IOException {
        // Parse the JSON tree to check for the schemaVersion property
        JsonNode node = p.readValueAsTree();
        JsonNode schemaVersionNode = node.get("schemaVersion");
        
        if (schemaVersionNode == null || schemaVersionNode.isNull()) {
            // Fallback to SubClassV1 when schemaVersion is missing
            return ctxt.readValue(node.traverse(), SubClassV1.class);
        } else {
            // Use default deserialization logic when schemaVersion exists
            return defaultDeserializer.deserializeTypedFromObject(node.traverse(), ctxt);
        }
    }
}

Step 2: Register the Custom Deserializer

Create a module to attach your custom deserializer to the SuperClass:

public class SuperClassModule extends SimpleModule {
    public SuperClassModule() {
        setMixInAnnotation(SuperClass.class, SuperClassMixIn.class);
    }

    private abstract static class SuperClassMixIn {
        @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "schemaVersion")
        @JsonSubTypes({
            @JsonSubTypes.Type(value = SubClassV1.class, name = "1.0"),
            @JsonSubTypes.Type(value = SubClassV2.class, name = "2.0")
        })
        private SuperClass superClass;
    }

    @Override
    public void setupModule(SetupContext context) {
        super.setupModule(context);
        // Get the default type deserializer for SuperClass
        TypeDeserializer defaultDeserializer = context.getTypeDeserializer(context.getTypeFactory().constructType(SuperClass.class));
        // Register the custom deserializer
        context.addBeanDeserializerModifier(new BeanDeserializerModifier() {
            @Override
            public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
                if (beanDesc.getBeanClass() == SuperClass.class) {
                    return new CustomSuperClassTypeDeserializer((TypeDeserializer) deserializer);
                }
                return deserializer;
            }
        });
    }
}

Step 3: Add the Module to Your ObjectMapper

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new SuperClassModule());

How it works:

  • This approach gives you full control over handling missing type properties—you can extend the logic to check other fields in the JSON to determine the appropriate subclass.
  • It maintains compatibility with the default deserialization flow when the type property is present.

Key Notes:

  • Ensure your subclasses (SubClassV1, SubClassV2) properly extend SuperClass and don’t override the schemaVersion field unless necessary.
  • If you want the schemaVersion field to always have a value even when missing from the JSON, initialize it in the SuperClass constructor or use @JsonSetter with a default value.

内容的提问来源于stack exchange,提问作者some random guy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 08:59:07