杰克逊 - 结合@JsonValue和@JsonSerialize

时间:2015-10-10 14:11:23

标签: java json dictionary jackson serializer

我正在尝试@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;
}

然而,这不是我想要的,因为这引入了另一个数据&#39;输出JSON中的级别。

是否可以以某种方式合并@JsonValue@JsonSerialize

我总是可以为Container编写另一个自定义序列化程序,它或多或少与@JsonValue背后的功能相同。在我看来,这或多或少是一种黑客行为。

杰克逊版本:2.6.2

2 个答案:

答案 0 :(得分:4)

这个组合似乎可以做你想要的:创建一个转换器从Container中提取Map,并将@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' }"));
}

我根本无法轻易地将Container的存取方法注释到DTRT,而不是与@JsonValue结合使用。鉴于容器上的@JsonValue基本上是指定一个转换器(通过调用带注释的方法实现),这实际上是你所追求的,虽然不像它应该的那样令人愉快。 (尝试过Jackson 2.6.2)

(我从中学到的东西:关键序列化器不像普通的序列化器,即使它们实现JsonSerializer也是如此。例如,它们需要在JsonGenerator上调用writeFieldName,而不是在writeString上调用。在反序列化方面, JsonDeserializer和KeyDeserializer之间的区别是拼写出来的,但不是在序列化方面。你可以使用@JsonValue从SomeKey创建一个关键的序列化器,但是通过使用@JsonSerialize注释SomeKey来 not (使用= ... ),这让我很吃惊。)

答案 1 :(得分:2)

您是否尝试过使用@JsonSerialize(using = SomeKeySerializer.class)代替keyUsing

Doc for using()说:

  

用于序列化关联值的Serializer类。

...而对于keyUsing,你得到:

  

用于序列化带注释属性的Map键的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"
}