Джексон: Добавить динамическое поле в сериализацию Map (например, @JsonAppend) - PullRequest
1 голос
/ 05 апреля 2019

Что-то вроде упрощенной версии @ JsonAppend

public class Bean {

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

было бы здорово - есть ли простой способ добиться этого?

Я прочитал много SO записей, например.

но не нашел ничего, что соответствовало бы моим потребностям.

Причина моего запроса в том, что он не различим, произошел ли какой-то данный JSON из сериализации Map или POJO. Если это необходимо (в редких случаях), добавление магического дополнительного поля на карту будет простым способом достижения этого.

1 Ответ

2 голосов
/ 05 апреля 2019

Отличный вопрос! Да, это (как-то) возможно. Следующая представленная методология поддерживает стандартное поведение сериализации, добавляя к нему пары пар ключ-значение, определенные аннотацией.


Создание пользовательской аннотации. Я назову это 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"}}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...