You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Jackson自定义反序列化器中获取输出字段的泛型类型

实现支持泛型的Jackson序列化/反序列化器处理ThirdPartyMap

看起来你想要让自定义的序列化/反序列化器具备泛型能力,不用硬编码MyKeyMyValue作为ThirdPartyMap的类型参数。下面是具体的实现方案,核心是利用Jackson的上下文序列化/反序列化器来动态获取泛型类型信息:

1. 泛型序列化器(MyGenericSerializer)

首先我们实现支持泛型的序列化器,解决默认用key.toString()作为字段名的问题。这里假设我们把ThirdPartyMap序列化成键值对数组(这样能完整保留键的结构化信息),你可以根据需求调整序列化格式:

public class MyGenericSerializer extends JsonSerializer<ThirdPartyMap<?, ?>> implements ContextualSerializer {
    private JavaType keyType;
    private JavaType valueType;

    // 无参构造器,用于Jackson初始化
    public MyGenericSerializer() {}

    // 带类型参数的构造器,用于上下文初始化时传递实际类型
    private MyGenericSerializer(JavaType keyType, JavaType valueType) {
        this.keyType = keyType;
        this.valueType = valueType;
    }

    @Override
    public void serialize(ThirdPartyMap<?, ?> value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        gen.writeStartArray();
        for (Map.Entry<?, ?> entry : value.entrySet()) {
            gen.writeStartObject();
            // 序列化键为结构化对象
            gen.writeFieldName("key");
            serializers.findValueSerializer(keyType).serialize(entry.getKey(), gen, serializers);
            // 序列化值
            gen.writeFieldName("value");
            serializers.findValueSerializer(valueType).serialize(entry.getValue(), gen, serializers);
            gen.writeEndObject();
        }
        gen.writeEndArray();
    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
        // 获取ThirdPartyMap的实际泛型类型参数
        JavaType mapType = property.getType();
        JavaType keyType = mapType.containedType(0);
        JavaType valueType = mapType.containedType(1);
        return new MyGenericSerializer(keyType, valueType);
    }
}

2. 泛型反序列化器(MyGenericDeserializer)

接下来是关键的泛型反序列化器,通过实现ContextualDeserializer来动态获取MyKeyMyValue的实际类型:

public class MyGenericDeserializer extends JsonDeserializer<ThirdPartyMap<?, ?>> implements ContextualDeserializer {
    private JavaType keyType;
    private JavaType valueType;

    // 无参构造器
    public MyGenericDeserializer() {}

    // 带类型参数的构造器
    private MyGenericDeserializer(JavaType keyType, JavaType valueType) {
        this.keyType = keyType;
        this.valueType = valueType;
    }

    @Override
    public ThirdPartyMap<?, ?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        ThirdPartyMap<Object, Object> map = new ThirdPartyMap<>();
        // 按数组格式反序列化(和序列化逻辑对应)
        JsonNode arrayNode = p.readValueAsTree();
        if (arrayNode.isArray()) {
            for (JsonNode node : arrayNode) {
                // 反序列化键
                Object key = ctxt.readTreeAsValue(node.get("key"), keyType);
                // 反序列化值
                Object value = ctxt.readTreeAsValue(node.get("value"), valueType);
                map.put(key, value);
            }
        }
        return map;
    }

    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
        // 获取ThirdPartyMap的泛型类型参数
        JavaType mapType = property.getType();
        JavaType keyType = mapType.containedType(0);
        JavaType valueType = mapType.containedType(1);
        return new MyGenericDeserializer(keyType, valueType);
    }
}

3. 调整Container类的注解

现在把原来的硬编码序列化/反序列化器替换成我们的泛型版本,修改后的Container类如下:

@JsonDeserialize(builder = ContainerBuilder.class)
class Container {
    @JsonSerialize(using = MyGenericSerializer.class)
    @JsonDeserialize(using = MyGenericDeserializer.class)
    ThirdPartyMap<MyKey, MyValue> map;

    // 私有构造器,由Builder创建
    private Container(ContainerBuilder builder) {
        this.map = builder.map;
    }

    // Builder类保持原有逻辑即可
    public static class ContainerBuilder {
        private ThirdPartyMap<MyKey, MyValue> map;

        public ContainerBuilder map(ThirdPartyMap<MyKey, MyValue> map) {
            this.map = map;
            return this;
        }

        public Container build() {
            return new Container(this);
        }
    }
}

4. 验证泛型能力

现在这个序列化/反序列化器可以支持任意类型参数的ThirdPartyMap,比如你如果有另一个类:

class AnotherContainer {
    @JsonSerialize(using = MyGenericSerializer.class)
    @JsonDeserialize(using = MyGenericDeserializer.class)
    ThirdPartyMap<String, Integer> anotherMap;
}

不需要修改序列化/反序列化器的代码,它会自动获取StringInteger作为类型参数进行处理。

关键说明

  • ContextualSerializer/ContextualDeserializer:这两个接口是实现泛型支持的核心,它们允许我们在序列化/反序列化时,从Bean属性的上下文信息中获取到实际的泛型类型参数,而不用提前硬编码。
  • JavaType:Jackson提供的类型封装类,用来表示各种Java类型(包括泛型),通过containedType(int index)方法可以获取泛型的类型参数。
  • 序列化格式:示例中把ThirdPartyMap序列化成键值对数组,你可以根据自己的需求调整(比如如果需要保留对象格式,可以把键序列化成JSON字符串,但这样可能丢失结构化信息,所以数组格式更适合保留键的结构化特征)。

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

火山引擎 最新活动