Jackson:向 Map 序列化添加动态字段(如 @JsonAppend)

bgr*_*ves 3 java serialization json dictionary jackson

类似于 @JsonAppend 的简化版本

public class Bean {

    @JsonAppend(key = [...], value = [...])
    public Map<?, ?> map = new HashMap<>();
}
Run Code Online (Sandbox Code Playgroud)

那就太好了-有什么简单的方法可以实现这一点吗?

我读过很多 SO 条目,例如。

但没有找到符合我需求的东西。

我的请求的原因是无法区分某些给定的 JSON 是源自 Map 还是 POJO 序列化。如果有必要(在极少数情况下),向地图添加魔法额外字段将是实现此目的的简单方法。

Lpp*_*Edd 5

好问题!是的,这是(以某种方式)可能的。以下公开的方法维护标准序列化行为,同时在其之上添加注释定义的键值对。


创建自定义注释。我会称之为MapAppender

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MapAppender {
    String[] keys();
    String[] values();
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我们定义了键值数组,它将按索引进行匹配。
我们被迫使用String字段而不是更通用的字段Object,但这是根据注释设计的。

创建自定义JsonSerializer<Map>. 我会称之为MapAppenderSerializer

public class MapAppenderSerializer
        extends StdSerializer<Map>
        implements ContextualSerializer {
    private static final long serialVersionUID = 1L;

    private final String[] keys;
    private final String[] values;

    // No-arg constructor required for Jackson
    MapAppenderSerializer() {
        super(Map.class);
        keys = new String[0];
        values = new String[0];
    }

    MapAppenderSerializer(
            final String[] keys,
            final String[] values) {
        super(Map.class);
        this.keys = keys;
        this.values = values;
    }

    @Override
    public void serialize(
            final Map value,
            final JsonGenerator jsonGenerator,
            final SerializerProvider serializerProvider) throws IOException {
        // Create a copy Map to avoid touching the original one
        final Map hashMap = new HashMap<>(value);

        // Add the annotation-specified key-value pairs
        for (int i = 0; i < keys.length; i++) {
            hashMap.put(keys[i], values[i]);
        }

        // Serialize the new Map
        serializerProvider.defaultSerializeValue(hashMap, jsonGenerator);
    }

    @Override
    public JsonSerializer<?> createContextual(
            final SerializerProvider serializerProvider,
            final BeanProperty property) {
        MapAppender annotation = null;

        if (property != null) {
            annotation = property.getAnnotation(MapAppender.class);
        }

        if (annotation != null) {
            return new MapAppenderSerializer(annotation.keys(), annotation.values());
        }

        throw new UnsupportedOperationException("...");
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,使用您的Bean类示例,使用注释该Map字段并@MapAppender使用定义自定义序列化器@JsonSerialize

public class Bean {
    public String simpleField;

    @MapAppender(keys = {"test1", "test2"}, values = {"value1", "value2"})
    @JsonSerialize(using = MapAppenderSerializer.class)
    public Map<Object, Object> simpleMap = new HashMap<>();
}
Run Code Online (Sandbox Code Playgroud)

就是这样。序列化一个实例Bean

final ObjectMapper objectMapper = new ObjectMapper();
final String string = objectMapper.writeValueAsString(new Bean());
Run Code Online (Sandbox Code Playgroud)

结果是

{"simpleField":null,"simpleMap":{"test2":"value2","test1":"value1"}}
Run Code Online (Sandbox Code Playgroud)

另一个例子,Map在序列化之前填充值

final ObjectMapper objectMapper = new ObjectMapper();
final Bean value = new Bean();
value.simpleMap.put("myKey", "myValue");

final String string = objectMapper.writeValueAsString(value);
Run Code Online (Sandbox Code Playgroud)

结果是

{"simpleField":null,"simpleMap":{"test1":"value1","test2":"value2","myKey":"myValue"}}
Run Code Online (Sandbox Code Playgroud)