如何为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
相关产品推荐
相关产品推荐

