2015-10-10 13 views
1

Я пробую комбинацию @JsonValue и @JsonSerialize. Давайте начнем с моим текущим классом контейнера:Jackson - комментировать @JsonValue и @JsonSerialize

public class Container { 
    private final Map<SomeKey, Object> data; 

    @JsonValue 
    @JsonSerialize(keyUsing = SomeKeySerializer.class) 
    public Map<SomeKey, Object> data() { 
     return data; 
    } 
} 

В этом случае обычай сериализатор SomeKeySerializer не используется.

Если изменить контейнер, как следующее, сериализатор называется:

public class Container { 
    @JsonSerialize(keyUsing = SomeKeySerializer.class) 
    private final Map<SomeKey, Object> data; 
} 

Однако, это не то, что я хочу, так как это вводит еще один уровень «данные» в выходных данных JSON.

Возможно ли совместить @JsonValue и @JsonSerialize в некотором роде?

Я всегда мог написать другой пользовательский сериализатор для Container, который более или менее аналогичен функциям позади @JsonValue. На мой взгляд, это будет более или менее взломанным.

версия Джексон: 2.6.2

+0

Как вы помещаете данные в 'private final Map data;'? – Filip

+0

На самом деле, я просто пытался воспроизвести это, и мне кажется, что использование @JsonValue не вызывает сериализатор, это просто вызов SomeKey.toString(). Разве это не то, что вы видите? – araqnid

+0

Точно, это мой вопрос :). –

ответ

3

Эта комбинация, кажется, что вы хотите: сделать конвертер для извлечения карты из контейнера, и добавить @JsonValue в SomeKey сам сериализовать его:

@JsonSerialize(converter = ContainerToMap.class) 
public class ContainerWithFieldData { 
    private final Map<SomeKey, Object> data; 

    public ContainerWithFieldData(Map<SomeKey, Object> data) { 
     this.data = data; 
    } 
} 

public static final class SomeKey { 
    public final String key; 

    public SomeKey(String key) { 
     this.key = key; 
    } 

    @JsonValue 
    public String toJsonValue() { 
     return "key:" + key; 
    } 

    @Override 
    public String toString() { 
     return "SomeKey:" + key; 
    } 
} 

public static final class ContainerToMap extends StdConverter<ContainerWithFieldData, Map<SomeKey, Object>> { 
    @Override 
    public Map<SomeKey, Object> convert(ContainerWithFieldData value) { 
     return value.data; 
    } 
} 

@Test 
public void serialize_container_with_custom_keys_in_field_map() throws Exception { 
    ObjectMapper mapper = new ObjectMapper(); 
    assertThat(
      mapper.writeValueAsString(new ContainerWithFieldData(ImmutableMap.of(new SomeKey("key1"), "value1"))), 
      equivalentTo("{ 'key:key1' : 'value1' }")); 
} 

Я просто не могу аннотирования акцесора из контейнера в DTRT совсем легко, а не в сочетании с @JsonValue. Учитывая, что @JsonValue на контейнере в любом случае обозначает конвертер (который реализуется путем вызова аннотированного метода), это фактически то, что вам нужно, хотя и не так приятно, как кажется. (пробовал с Jackson 2.6.2)

(Что-то я узнал из этого: ключевые сериализаторы не похожи на обычные сериализаторы, хотя они одинаково реализуют JsonSerializer. Они должны вызывать writeFieldName на JsonGenerator, а не writeString, для пример.На стороне десериализации различие между JsonDeserializer и KeyDeserializer указано, но не на стороне сериализации. Вы можете сделать ключевой сериализатор из SomeKey с @JsonValue, но не путем аннотации SomeKey с помощью @JsonSerialize (using = ...), что меня удивило).

+0

Спасибо, это имеет смысл. Я расскажу о своем вопросе для трекера Jackson, поскольку мы, по моему мнению, работаем над ограничениями '@ JsonValue' в сочетании с другими аннотациями. –

+0

О твоем редактировании: действительно! Я заметил то же самое. До сих пор я понятия не имею, почему нет «KeySerializer», обратного «KeyDeserializer». –

+0

Я могу немного помочь с этой загадкой - в момент добавления пользовательских обработчиков ключей было возможно использовать 'JsonSerializer' для ключевых сериализаторов (может быть вызван таким же образом), но это не так для' JsonDeserializer'. Пытаясь избежать добавления многих новых классов, добавлен только один. Оглядываясь назад, вероятно, было бы целесообразнее добавить «KeySerializer», чтобы предотвратить сюрпризы. – StaxMan

1

Вы пробовали использовать @JsonSerialize(using = SomeKeySerializer.class) вместо keyUsing?

Doc for using() говорит:

Serializer класс использовать для сериализации соответствующего значения.

... в то время как для keyUsing вы получите:

Serializer класс использовать для сериализации Карта ключи аннотированный недвижимости

Испытано его из себя, и он работает ...

public class Demo { 

    public static class Container { 

    private final Map<String, String> data = new HashMap<>(); 

    @JsonValue 
    @JsonSerialize(using = SomeKeySerializer.class) 
    public Map<String, String> data() { 
     return data; 
    } 
    } 

    public static class SomeKeySerializer extends JsonSerializer<Map> { 

    @Override 
    public void serialize(Map value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { 
     jgen.writeStartObject(); 
     jgen.writeObjectField("aKeyInTheMap", "theValueForThatKey"); 
     jgen.writeEndObject(); 
    } 
    } 

    public static void main(String[] args) throws JsonProcessingException { 
    ObjectMapper objectMapper = new ObjectMapper(); 
    objectMapper.enable(SerializationFeature.INDENT_OUTPUT); 
    String s = objectMapper.writeValueAsString(new Container()); 
    System.out.println(s); 
    } 
} 

Это результат, когда я НЕ использую com.fasterxml.jackson.annotation.JsonValue

{ 
    "data" : { 
    "aKeyInTheMap" : "theValueForThatKey" 
    } 
} 

И это выход, когда я использую com.fasterxml.jackson.annotation.JsonValue

{ 
    "aKeyInTheMap" : "theValueForThatKey" 
} 
+0

Спасибо. Это действительно работает, но это не так. Разве мы не просто заменяем сериализатор карты по умолчанию нашим пользовательским сериализатором? –

+0

Думаю, что так ... если бы у меня была какая-то комната для «метафизики» :) – Filip

+0

Я также наткнулся на [JsonAnyGetter не работает с JsonSerialize (за исключением keyUsing)] (https://github.com/ FasterXML/jackson-databind/issues/705), и он может применяться к вашей конкретной проблеме, однако он предполагает несколько иной подход, поскольку он использует 'com.fasterxml.jackson.databind.util.Converter' вместо' com.fasterxml.jackson .databind.JsonSerializer' ... – Filip