Immutable Lombok annotated class with Jackson

爷,独闯天下 提交于 2019-12-09 14:31:00

问题


What is the preferred way to create class that is

  • Immutable
  • Can be serialized/deserialized with Jackson
  • Human-readable and with low level of boilerplate

Preferably, I would have liked something like this to work:

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

and then have all fields to be private final. However, this does not even compile (and I'm not sure why). Using

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

will compile but only yields

InvalidDefinitionException: No serializer found for class

回答1:


Another alternative which:

  • meets the criteria
  • has less boilerplace than the current top answer
  • works on v1.16.20 (January 9th, 2018) and later

is to add ConstructorProperties:

  • Create a lombok.config file in an appropriate location with the line: lombok.anyConstructor.addConstructorProperties = true
  • Add lombok @Value annotation to your class to make it immutable

Then serialization and deserialization by Jackson works as expected.




回答2:


You can use Lombok's @Builder annotation to generate a builder for your immutable POJO class. But making the Lombok-generated builder usable by Jackson's deserialization is somewhat tricky.

  • You need to annotate your POJO class with @JsonDeserialize(builder = ...) to tell Jackson which is the builder class to use.
  • You need to annotate the builder class with @JsonPOJOBuilder(withPrefix = "") to tell Jackson that its setter-methods do not start with with.

Example:

An immutable 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
    }
}

Here is a JUnit test to verify the 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);
    }
}



回答3:


Another alternative that is much less verbose:

@Data
@Setter(AccessLevel.NONE)
public class Clazz {
    private String field;
} 

Of course, you could still have some private method that modifies the field directly, but it would be very unlikely to even have any actual code in a @Data POJO, so this would hopefully not happen.

Disclaimer: This will have the side-effect (perhaps beneficial) of not letting regular Java code create the object since there is only a default constructor with no mutators. To allow for normal construction you will need 2 more annotations:

@Data
@AllArgsConstructor
@NoArgsConstructor
@Setter(AccessLevel.NONE)
public class Clazz {
    private String field;
} 



回答4:


By referencing answer by Joseph K. Strauss I came up with following solution.

Plain lombok annotations that worked out for me looks like this. Following annotations give you immutable classes with builder that can be serialized and deserialized by Jackson.

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

I prefer this solution because it requires no additional Jackson specific annotations and no additional lombok specific files



来源:https://stackoverflow.com/questions/49999492/immutable-lombok-annotated-class-with-jackson

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!