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

如何为Jackson类型层级定义含多个JsonCreator的Mixin?

问题描述

想要用Jackson反序列化部分Hamcrest匹配器,创建了如下Mixin:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "_type")
@JsonSubTypes({ @Type(value = HasPropertyWithValue.class, name = "hasPropertyWithValue"),
        @Type(value = IsEqual.class, name = "equalTo") })
public class HamcrestMixIn {

    @JsonCreator
    public static <T> HasPropertyWithValue<T> hasPropertyWithValue(@JsonProperty("property") String property,
            @JsonProperty("valueMatcher") Matcher<T> valueMatcher) {
        return new HasPropertyWithValue<>(property, valueMatcher);
    }

    @JsonCreator
    public static <T> IsEqual<T> isEqualTo(@JsonProperty("value") T value) {
        return new IsEqual<>(value);
    }
}

已在Spring Boot应用中通过声明包含该Mixin的SimpleModule Bean完成注册。但反序列化以下JSON时:

{
    "_type": "hasPropertyWithValue",
    "property": "foo",
    "valueMatcher": {
        "_type": "equalTo",
        "value": "bar"
    }
}

抛出错误:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.hamcrest.beans.HasPropertyWithValue` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (BufferedInputStream); line: 3, column: 2]
    at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
    at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1909)
    at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:408)
    at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1354)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1420)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:352)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:220)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:187)
    at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedForId(AsPropertyTypeDeserializer.java:144)
    at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:110)
    at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserializeWithType(AbstractDeserializer.java:263)
    at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:74)
    at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4730)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3714)

需要修改Mixin,使得上述JSON能反序列化为:

HamcrestMixIn.hasPropertyWithValue("foo", HamcrestMixIn.isEqualTo("bar"))
解决方案

问题核心是原Mixin未将静态工厂方法与对应的Hamcrest类绑定,Jackson无法识别这些方法是用来构造哪个类的实例。以下是正确的修改方案:

拆分独立Mixin类(推荐)

为每个Hamcrest匹配器类单独创建Mixin,明确绑定关系:

// HasPropertyWithValue对应的Mixin
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "_type")
@JsonTypeName("hasPropertyWithValue")
public abstract class HasPropertyWithValueMixIn<T> {
    @JsonCreator
    public static <T> HasPropertyWithValue<T> create(@JsonProperty("property") String property,
                                                     @JsonProperty("valueMatcher") Matcher<T> valueMatcher) {
        return new HasPropertyWithValue<>(property, valueMatcher);
    }
}

// IsEqual对应的Mixin
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "_type")
@JsonTypeName("equalTo")
public abstract class IsEqualMixIn<T> {
    @JsonCreator
    public static <T> IsEqual<T> create(@JsonProperty("value") T value) {
        return new IsEqual<>(value);
    }
}

注册模块到Spring Boot

在配置类中声明模块Bean,将Mixin与目标类绑定:

@Bean
public Module hamcrestModule() {
    SimpleModule module = new SimpleModule();
    module.setMixInAnnotation(HasPropertyWithValue.class, HasPropertyWithValueMixIn.class);
    module.setMixInAnnotation(IsEqual.class, IsEqualMixIn.class);
    return module;
}

关键说明

  • 每个Mixin类通过@JsonTypeName指定JSON中_type字段对应的值,确保Jackson能匹配到正确的类。
  • @JsonCreator标记的静态工厂方法,明确告诉Jackson用该方法构造对应Hamcrest类的实例,@JsonProperty负责映射JSON字段到方法参数。
  • 通过setMixInAnnotation将Mixin与目标Hamcrest类绑定,让Jackson知道该用哪个Mixin的规则来反序列化目标类。

内容的提问来源于stack exchange,提问作者SpaceTrucker

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 08:59:13