如何在不嵌套的情况下使用 Jackson 序列化单值 @Data 对象(例如 {"id":123},而不是 {"id":{"value":123}})?
How to serialize single-valued @Data object using Jackson without nesting (e.g. {"id":123}, not {"id":{"value":123}})?
我计划使用 Lombok 创建数百个基于 "value object" 模式的 classes:
@Data
public final class SomeId implements Serializable {
private final long value;
}
我想将这些 classes 用于由 Jackson 提供支持的 JSON 序列化。例如,考虑一个 DTO class 如下:
public class SomeDTO {
SomeId id;
public SomeId getId() {
return id;
}
}
我希望将 DTO class 序列化为 {"id":123}
之类的东西,但 Jackson 生成了 {"id":{"value":123}}
之类的东西,它带有一个不必要的嵌套对象,其中包含一个名为 [=18] 的字段=].即一个测试用例表达了我的要求:
public class SomeDTOTest {
@Test
public void serializationTest() throws Exception {
SomeDTO dto = new SomeDTO();
dto.id = new SomeId(123);
String serialized = new ObjectMapper().writeValueAsString(dto);
System.out.println(serialized); // {"id":{"value":123}}
assertThat(serialized, is("{\"id\":123}")); // I want {"id":123} instead!
}
}
我知道在 SomeId
classes 中的每个 getValue()
方法中添加 @JsonValue
注释将是一个解决方案,但我不能这样做,因为没有@Data
classes 的实际定义,因为 Lombok 会自动创建它。
为每个 classes 手动创建注释为 @JsonValue
的实际 getValue()
方法可能是另一种解决方案,但这意味着创建大量样板代码。
如何在没有样板代码的情况下实现此要求?
您可以为此编写自定义序列化程序 class。
例如:
class CustomSerializer extends StdSerializer<SomeId>{
protected CustomSerializer(Class<SomeId> t) {
super(t);
}
@Override
public void serialize(SomeId someId, JsonGenerator gen, SerializerProvider serializers)
throws IOException, JsonProcessingException {
gen.writeNumber(someId.getValue());
}
}
现在使用这个序列化器来序列化SomeId
class:
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(new CustomSerializer(SomeId.class));
mapper.registerModule(module);
SomeDTO dto = new SomeDTO();
dto.id = new SomeId(123);
String serialized = mapper.writeValueAsString(dto);
System.out.println(serialized); // output : {"id":123}
我创建了一个接口 getValue()
注释为 @JsonValue
:
public interface LongValue {
@JsonValue
long getValue();
}
然后在每个 @Data
类 中实施它。请注意,getValue()
的实际实现将由 Lombok 自动生成:
@Data
public final class SomeId implements LongValue, Serializable {
private final long value;
}
这样我就通过了测试 SomeDTOTest
- SomeDTO
如我所料序列化为 {"id":123}
。
我计划使用 Lombok 创建数百个基于 "value object" 模式的 classes:
@Data
public final class SomeId implements Serializable {
private final long value;
}
我想将这些 classes 用于由 Jackson 提供支持的 JSON 序列化。例如,考虑一个 DTO class 如下:
public class SomeDTO {
SomeId id;
public SomeId getId() {
return id;
}
}
我希望将 DTO class 序列化为 {"id":123}
之类的东西,但 Jackson 生成了 {"id":{"value":123}}
之类的东西,它带有一个不必要的嵌套对象,其中包含一个名为 [=18] 的字段=].即一个测试用例表达了我的要求:
public class SomeDTOTest {
@Test
public void serializationTest() throws Exception {
SomeDTO dto = new SomeDTO();
dto.id = new SomeId(123);
String serialized = new ObjectMapper().writeValueAsString(dto);
System.out.println(serialized); // {"id":{"value":123}}
assertThat(serialized, is("{\"id\":123}")); // I want {"id":123} instead!
}
}
我知道在 SomeId
classes 中的每个 getValue()
方法中添加 @JsonValue
注释将是一个解决方案,但我不能这样做,因为没有@Data
classes 的实际定义,因为 Lombok 会自动创建它。
为每个 classes 手动创建注释为 @JsonValue
的实际 getValue()
方法可能是另一种解决方案,但这意味着创建大量样板代码。
如何在没有样板代码的情况下实现此要求?
您可以为此编写自定义序列化程序 class。
例如:
class CustomSerializer extends StdSerializer<SomeId>{
protected CustomSerializer(Class<SomeId> t) {
super(t);
}
@Override
public void serialize(SomeId someId, JsonGenerator gen, SerializerProvider serializers)
throws IOException, JsonProcessingException {
gen.writeNumber(someId.getValue());
}
}
现在使用这个序列化器来序列化SomeId
class:
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(new CustomSerializer(SomeId.class));
mapper.registerModule(module);
SomeDTO dto = new SomeDTO();
dto.id = new SomeId(123);
String serialized = mapper.writeValueAsString(dto);
System.out.println(serialized); // output : {"id":123}
我创建了一个接口 getValue()
注释为 @JsonValue
:
public interface LongValue {
@JsonValue
long getValue();
}
然后在每个 @Data
类 中实施它。请注意,getValue()
的实际实现将由 Lombok 自动生成:
@Data
public final class SomeId implements LongValue, Serializable {
private final long value;
}
这样我就通过了测试 SomeDTOTest
- SomeDTO
如我所料序列化为 {"id":123}
。