问题
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