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

如何用Jackson将含未知属性名的JSON反序列化为泛型类型

Jackson泛型类映射动态属性到指定字段问题解决

问题场景

需要反序列化包含固定id字段和一个动态属性名(如human/cat/bike)的JSON,动态属性的类型编译时已知,目标是将该动态属性映射到泛型类Data<T>的other字段。现有代码因Jackson无法识别动态属性名,抛出错误:

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "human"

解决方案

方案1:使用@JsonAnySetter捕获动态属性

针对@Value生成的不可变类,添加@JsonAnySetter注解的方法接收所有未知属性,并赋值给other字段:

@Value
@Builder
@Jacksonized
public class Data<T> {
    String id;
    T other;

    // 接收所有未知属性,将值赋值给other
    @JsonAnySetter
    private void setOther(String key, T value) {
        this.other = value;
    }
}

使用时必须通过TypeReference保留泛型信息,否则Jackson无法推断T的具体类型:

Data<Human> humanData = objectMapper.readValue(jsonHumanString, new TypeReference<Data<Human>>() {});
Data<Cat> catData = objectMapper.readValue(jsonCatString, new TypeReference<Data<Cat>>() {});

方案2:自定义泛型反序列化器

实现自定义反序列化器,手动解析JSON并提取动态属性:

public class DataDeserializer<T> extends JsonDeserializer<Data<T>> {
    private final Class<T> targetType;

    public DataDeserializer(Class<T> targetType) {
        this.targetType = targetType;
    }

    @Override
    public Data<T> deserialize(JsonParser parser, DeserializationContext context) throws IOException {
        JsonNode rootNode = parser.getCodec().readTree(parser);
        String id = rootNode.get("id").asText();
        
        // 遍历除id外的字段,取第一个动态属性
        T other = null;
        for (Iterator<String> fieldNames = rootNode.fieldNames(); fieldNames.hasNext(); ) {
            String fieldName = fieldNames.next();
            if (!"id".equals(fieldName)) {
                JsonNode otherNode = rootNode.get(fieldName);
                other = parser.getCodec().treeToValue(otherNode, targetType);
                break;
            }
        }
        
        return Data.<T>builder().id(id).other(other).build();
    }
}

注册反序列化器并使用:

// 注册针对Human类型的反序列化器
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Data.class, new DataDeserializer<>(Human.class));
mapper.registerModule(module);

Data<Human> humanData = mapper.readValue(jsonHumanString, Data.class);

方案3:结合@JsonIgnoreProperties与泛型类型推断

先允许Jackson忽略未知属性,再通过反射获取泛型实际类型,将动态属性转换为T类型:

@Value
@Builder
@Jacksonized
@JsonIgnoreProperties(ignoreUnknown = true)
public class Data<T> {
    String id;
    T other;

    @JsonAnySetter
    private void parseOther(String key, JsonNode value) throws JsonProcessingException {
        // 通过反射获取泛型实际类型
        Class<T> type = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        this.other = new ObjectMapper().treeToValue(value, type);
    }
}

使用时同样需要用TypeReference保留泛型信息:

Data<Bike> bikeData = objectMapper.readValue(jsonBikeString, new TypeReference<Data<Bike>>() {});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 00:12:49