Jackson:将动态字段添加到Map序列化中(例如@JsonAppend)

时间:2019-04-04 21:02:54

标签: java json dictionary serialization jackson

类似于@JsonAppend的简化版本

public class Bean {

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

会很棒-有任何简单的方法可以做到这一点吗?

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

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

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

1 个答案:

答案 0 :(得分:2)

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


创建一个自定义注释。我称它为MapAppender

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

如您所见,我们定义了键值数组,它们将按索引进行匹配。
我们被迫使用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("...");
    }
}

现在,使用您的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<>();
}

就是这样。序列化Bean

的实例
final ObjectMapper objectMapper = new ObjectMapper();
final String string = objectMapper.writeValueAsString(new Bean());

产生

{"simpleField":null,"simpleMap":{"test2":"value2","test1":"value1"}}

另一个示例,在序列化之前将Map填充为值

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

final String string = objectMapper.writeValueAsString(value);

产生

{"simpleField":null,"simpleMap":{"test1":"value1","test2":"value2","myKey":"myValue"}}