不可变 Lombok 由 Jackson 注释 class

Immutable Lombok annotated class with Jackson

创建 class 的首选方法是

最好,我希望这样的东西能工作:

@Data(onConstructor = @__(@JsonCreator))

然后将所有字段设置为 private final。但是,这甚至无法编译(我不确定为什么)。使用

@AllArgsConstructor(onConstructor = @__(@JsonCreator))

将编译但只产生

InvalidDefinitionException: No serializer found for class

您可以使用 Lombok 的 @Builder 注释为您的不可变 POJO class 生成构建器。 但是让 Jackson 的反序列化可以使用 Lombok-generated 构建器有点棘手。

示例:

不可变的 POJO class:

@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 测试来验证 serialization/deserialization:

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);
    }
}

另一个不那么冗长的选择:

@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;
} 

添加ConstructorProperties:

  • 使用以下行创建一个 lombok.config 文件 in an appropriate locationlombok.anyConstructor.addConstructorProperties = true
  • 将 lombok @Value 注释添加到您的 class 以使其不可变

然后 Jackson 的序列化和反序列化按预期工作。

此方法:

编辑:2020-08-16

  • 注意:将 @Builder@Value 一起使用会导致此解决方案失败。 (感谢下面@guilherme-blanco 的评论。) 但是,如果您还添加例如@AllArgsConstructor 它仍然按预期工作。

编辑:2021-08-19

  • 注意:当您添加或更改 lombok.config 文件时,除非您进行重建(清理然后构建),否则更改不会被应用。我已经被这个抓住了几次。
  • 是为特定 classes 注释实现预期结果的另一种方法。但是,我个人更喜欢不需要记住注释每个用于反序列化的 class 。使用 lombok.config 消除了这种开销。

通过参考 我想出了以下解决方案。

对我有用的普通 lombok 注释看起来像这样。以下注释为您提供了不可变 类 和可以由 Jackson 序列化和反序列化的构建器。

    @Data
    @Setter(AccessLevel.NONE)
    @Builder(toBuilder = true)
    @AllArgsConstructor
    @NoArgsConstructor(access = AccessLevel.PRIVATE)
    public class Clazz {
        private String field;
    } 

我更喜欢这个解决方案,因为它不需要额外的 and no additional

在 pom 中添加 Jackson Dataformat 依赖项后,Thomas Fritsch 的回答与 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>

为您的 pojo 尝试以下一组注释:

@Value
@NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
@AllArgsConstructor

我刚刚用这个方法解决了它:

@Value
@Builder(setterPrefix = "with")
@JsonDeserialize(builder = Clazz.ClazzBuilder.class)
public class Clazz {
    private String field;
}

在这种情况下,您必须使用像 withField(...) 这样的构建器方法,这是 jackson 使用的默认行为。

截至 2020 年 10 月 15 日(Lombok v1.18.16), you should simply be able to use the @Jacksonized 注释。

@Jacksonized @Builder
@JsonIgnoreProperties(ignoreUnknown = true)
public class JacksonExample {
  private List<Foo> foos;
}

如链接文档中所述,此注释:

  • 将 Jackson 配置为使用构建器进行反序列化,
  • 将特定字段的配置从带注释的 class 复制到生成的构建器(例如 @JsonIgnoreProperties),并且
  • 将构建器方法中使用的 Jackson 前缀(例如 builder().withField(field)builder.field(field) 与 Lombok 中配置的前缀对齐。

为 Jackson 配置不可变 class 的最简单方法是使用 lombok 注释:@Value and @Jacksonized:

    @Jacksonized
    @Builder
    @Value
    class Foo {

        
    }