简体   繁体   中英

Immutable Lombok annotated class with Jackson

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

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.

This method:

Edit: 2020-08-16

  • Note: Using @Builder with @Value causes this solution to fail. (Thanks to comment from @guilherme-blanco below.) However, if you also add eg @AllArgsConstructor it does still work as expected.

Edit: 2021-08-19

  • Note: When you add or change a lombok.config file the change is not picked up unless you do a rebuild (clean then build). I have been caught out by this a few times.
  • @Jacksonized annotation solution is another method to achieve the desired outcome for the specific classes annotated. However, I personally prefer not to need to remember to annotate every class used for deserialization. Using lombok.config removes this overhead.

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.

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

POJO大纲

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

As of 15 October 2020 ( Lombok v1.18.16 ), you should simply be able to use the @Jacksonized annotation.

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

As described in the linked documentation, this annotation:

  • configures Jackson to use the builder for deserialisation,
  • copies field-specific configuration down from the annotated class to the generated builder (eg @JsonIgnoreProperties ), and
  • aligns the Jackson prefix used on builder methods (eg builder().withField(field) vs builder.field(field) to the one configured in Lombok.

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

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

Try the following set of annotations for your pojo:

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

The simplest way to configure immutable class for Jackson is to use lombok annotation: @Value and @Jacksonized :

    @Jacksonized
    @Builder
    @Value
    class Foo {

        
    }

I've just solved it using this way:

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

In that case, you have to use the builder methods like withField(...) , which is the default behaviour used by jackson.

Answer by Thomas Fritsch worked perfectly with Spring Boot after adding Jackson Dataformat dependency in pom.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM