Jackson - 结合@JsonValue 和@JsonSerialize
Jackson - combine @JsonValue and @JsonSerialize
我正在尝试 @JsonValue
和 @JsonSerialize
的组合。让我们从我当前的容器开始 class:
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;
}
然而,这不是我想要的,因为这在输出中引入了另一个 'data' 级别 JSON。
是否可以通过某种方式组合 @JsonValue
和 @JsonSerialize
?
我总是可以为 Container
编写另一个自定义序列化程序,它或多或少与 @JsonValue
背后的功能相同。在我看来,这或多或少是一种黑客行为。
杰克逊版本:2.6.2
您是否尝试过使用 @JsonSerialize(using = SomeKeySerializer.class)
而不是 keyUsing
?
Serializer class to use for serializing associated value.
...while for keyUsing 你得到:
Serializer class to use for serializing Map keys of annotated property
我亲自测试过,它有效...
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"
}
这个组合似乎做你想做的事:制作一个转换器从容器中提取地图,并将@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 创建密钥序列化程序,但是 not 通过使用 @JsonSerialize(using= ...),这让我很惊讶)。
我正在尝试 @JsonValue
和 @JsonSerialize
的组合。让我们从我当前的容器开始 class:
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;
}
然而,这不是我想要的,因为这在输出中引入了另一个 'data' 级别 JSON。
是否可以通过某种方式组合 @JsonValue
和 @JsonSerialize
?
我总是可以为 Container
编写另一个自定义序列化程序,它或多或少与 @JsonValue
背后的功能相同。在我看来,这或多或少是一种黑客行为。
杰克逊版本:2.6.2
您是否尝试过使用 @JsonSerialize(using = SomeKeySerializer.class)
而不是 keyUsing
?
Serializer class to use for serializing associated value.
...while for keyUsing 你得到:
Serializer class to use for serializing Map keys of annotated property
我亲自测试过,它有效...
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"
}
这个组合似乎做你想做的事:制作一个转换器从容器中提取地图,并将@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 创建密钥序列化程序,但是 not 通过使用 @JsonSerialize(using= ...),这让我很惊讶)。