带有 Jackson 的不可变 Lombok 注释类
Posted
技术标签:
【中文标题】带有 Jackson 的不可变 Lombok 注释类【英文标题】:Immutable Lombok annotated class with Jackson 【发布时间】:2018-10-04 14:03:37 【问题描述】:创建类的首选方法是什么
不可变 可以用 Jackson 序列化/反序列化 人类可读且样板级别低最好,我希望这样的工作:
@Data(onConstructor = @__(@JsonCreator))
然后将所有字段设为private final
。然而,这甚至没有编译(我不知道为什么)。使用
@AllArgsConstructor(onConstructor = @__(@JsonCreator))
会编译,但只会产生
InvalidDefinitionException: No serializer found for class
【问题讨论】:
【参考方案1】:您可以使用 Lombok 的 @Builder
注释为您的不可变 POJO 类生成构建器。
但是让 Jackson 的反序列化可以使用 Lombok 生成的构建器有点棘手。
@JsonDeserialize(builder = ...)
注释你的POJO类
告诉 Jackson 要使用哪个构建器类。
您需要使用@JsonPOJOBuilder(withPrefix = "")
注释构建器类
告诉 Jackson 它的 setter 方法不以 with
开头。
例子:
一个不可变的 POJO 类:
@Data
@Builder(builderClassName = "PointBuilder")
@JsonDeserialize(builder = Point.PointBuilder.class)
public class Point
private final int x;
private final int y;
@JsonPOJOBuilder(withPrefix = "")
public static class PointBuilder
// Lombok will add constructor, setters, build method
这是一个验证序列化/反序列化的 JUnit 测试:
public class PointTest extends Assert
private ObjectMapper objectMapper = new ObjectMapper();
@Test
public void testSerialize() throws IOException
Point point = new Point(10, 20);
String json = objectMapper.writeValueAsString(point);
assertEquals("\"x\":10,\"y\":20", json);
@Test
public void testDeserialize() throws IOException
String json = "\"x\":10,\"y\":20";
Point point = objectMapper.readValue(json, Point.class);
assertEquals(new Point(10, 20), point);
【讨论】:
我已经使用这种方法有一段时间了,它非常非常有效。一个非常小的改进是我总是将我的构建器类命名为 _Builder。这样,当我复制粘贴以创建新类等时......我不必记住更改builderClassName中的字符串。我曾经调用类 Builder(不带下划线),但是当您使用内部静态类执行此操作时,这与 @Builder 注释类有奇怪的冲突。【参考方案2】:另一个不那么冗长的替代方案:
@Data
@Setter(AccessLevel.NONE)
public class Clazz
private String field;
当然,您仍然可以有一些直接修改字段的私有方法,但在 @Data POJO 中甚至不太可能有任何实际代码,因此希望不会发生这种情况。
免责声明: 这将产生不让常规 Java 代码创建对象的副作用(也许是有益的),因为只有一个没有修改器的默认构造函数。为了允许正常构建,您还需要 2 个注释:
@Data
@AllArgsConstructor
@NoArgsConstructor
@Setter(AccessLevel.NONE)
public class Clazz
private String field;
【讨论】:
【参考方案3】:添加 ConstructorProperties:
创建一个lombok.config
文件in an appropriate location,其中包含以下行:
lombok.anyConstructor.addConstructorProperties = true
将 lombok @Value
注释添加到您的类以使其不可变
然后 Jackson 的序列化和反序列化按预期工作。
这个方法:
符合标准 比以前的最佳答案少了样板 适用于 v1.16.20 (January 9th, 2018) 及更高版本编辑:2020-08-16
注意:将@Builder
与@Value
一起使用会导致此解决方案失败。 (感谢下面@guilherme-blanco 的评论。)
但是,如果您还添加例如@AllArgsConstructor
它仍然按预期工作。
编辑:2021-08-19
注意:当您添加或更改lombok.config
文件时,除非您进行重建(清理然后构建),否则不会获取更改。我已经被这个发现了好几次了。
@Jacksonized
annotation solution 是另一种方法,可以为注释的特定类实现预期结果。但是,我个人更喜欢不需要记住对用于反序列化的每个类进行注释。使用 lombok.config
可以消除这种开销。
【讨论】:
注意:此方法不能与 @Builder 结合使用。【参考方案4】:通过引用answer by Joseph K. Strauss,我想出了以下解决方案。
对我有用的普通 lombok 注释看起来像这样。以下注释为您提供了具有生成器的不可变类,可以由 Jackson 序列化和反序列化。
@Data
@Setter(AccessLevel.NONE)
@Builder(toBuilder = true)
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Clazz
private String field;
我更喜欢这个解决方案,因为它不需要额外的Jackson specific annotations,也不需要额外的lombok specific files
【讨论】:
谢谢。但似乎:【参考方案5】:Thomas Fritsch 的回答在 pom 中添加 Jackson Dataformat 依赖项后与 Spring Boot 完美配合。
@Data
@Builder(builderClassName = "PointBuilder")
@JsonDeserialize(builder = Point.PointBuilder.class)
public class Point
private final int x;
private final int y;
@JsonPOJOBuilder(withPrefix = "")
public static class PointBuilder
// Lombok will add constructor, setters, build method
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-xml -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
【讨论】:
【参考方案6】:为您的 pojo 尝试以下注释集:
@Value
@NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
@AllArgsConstructor
【讨论】:
【参考方案7】:我刚刚用这种方法解决了:
@Value
@Builder(setterPrefix = "with")
@JsonDeserialize(builder = Clazz.ClazzBuilder.class)
public class Clazz
private String field;
在这种情况下,您必须使用像 withField(...)
这样的构建器方法,这是 jackson 使用的默认行为。
【讨论】:
【参考方案8】:自 2020 年 10 月 15 日 (Lombok v1.18.16) 起,您应该可以使用 @Jacksonized
注释。
@Jacksonized @Builder @JsonIgnoreProperties(ignoreUnknown = true) public class JacksonExample private List<Foo> foos;
如链接文档中所述,此注释:
将 Jackson 配置为使用构建器进行反序列化, 将特定于字段的配置从带注释的类复制到生成的构建器(例如@JsonIgnoreProperties
),并且
将生成器方法中使用的 Jackson 前缀(例如 builder().withField(field)
与 builder.field(field)
与 Lombok 中配置的前缀对齐。
【讨论】:
我们可以把这个答案放在第一位吗?它就像一个魅力:)【参考方案9】:为 Jackson 配置不可变类的最简单方法是使用 lombok 注解:@Value
和 @Jacksonized
:
@Jacksonized
@Builder
@Value
class Foo
【讨论】:
以上是关于带有 Jackson 的不可变 Lombok 注释类的主要内容,如果未能解决你的问题,请参考以下文章
带有杰克逊 JsonProperty 的 Lombok 构建器模式