【问题标题】:Immutable Lombok annotated class with Jackson带有 Jackson 的不可变 Lombok 注释类
【发布时间】:2018-10-04 14:03:37
【问题描述】:

创建类的首选方法是什么

  • 不可变
  • 可以用 Jackson 序列化/反序列化
  • 人类可读且样板级别低

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

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

然后将所有字段设为private final。然而,这甚至没有编译(我不知道为什么)。使用

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

会编译,但只会产生

InvalidDefinitionException: No serializer found for class

【问题讨论】:

    标签: java json jackson lombok jackson-databind


    【解决方案1】:

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

    例子:

    一个不可变的 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 的序列化和反序列化按预期工作。

      这个方法:

      编辑: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 {
            
                    
                }
            

            【讨论】:

              猜你喜欢
              • 2018-07-16
              • 1970-01-01
              • 1970-01-01
              • 2019-10-08
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-09-20
              相关资源
              最近更新 更多