Неизменный Ломбок аннотировал класс с Джексоном
Каков предпочтительный способ создания класса?
- Неизменный
- Может быть сериализовано / десериализовано с Джексоном
- Человекочитаемый и с низким уровнем шаблона
Желательно, чтобы мне понравилось что-то подобное:
@Data(onConstructor = @__(@JsonCreator))
а затем все поля должны быть private final
, Тем не менее, это даже не компилируется (и я не уверен, почему). С помощью
@AllArgsConstructor(onConstructor = @__(@JsonCreator))
скомпилирует но только даст
InvalidDefinitionException: No serializer found for class
3 ответа
Вы можете использовать Ломбок @Builder
аннотация для генерации компоновщика для вашего неизменного класса POJO. Но сделать сгенерированный Ломбоком конструктор, пригодный для десериализации Джексона, довольно сложно.
- Вы должны аннотировать свой класс POJO
@JsonDeserialize(builder = ...)
сказать Джексону, какой класс строителей использовать. - Вы должны аннотировать класс строителя с помощью
@JsonPOJOBuilder(withPrefix = "")
сказать Джексону, что его методы установки не начинаются сwith
,
Пример:
Неизменный класс 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);
}
}
Еще одна альтернатива, которая:
- соответствует критериям
- имеет меньше котла, чем текущий топ-ответ
- работает над версией 16.16 (9 января 2018 г.) и позже
это добавить ConstructorProperties:
- Создать
lombok.config
файл в соответствующем месте со строкой:lombok.anyConstructor.addConstructorProperties = true
- Добавить ломбок
@Value
аннотация для вашего класса, чтобы сделать его неизменным
Затем сериализация и десериализация Джексона работает, как и ожидалось.
Начиная с 15 октября 2020 года Lombok (v1.18.16), вы должны просто иметь возможность использовать @Jacksonized
аннотация.
@Jacksonized @Builder @JsonIgnoreProperties(ignoreUnknown = true) public class JacksonExample { private List<Foo> foos; }
Как описано в связанной документации, эта аннотация:
- настраивает Джексона на использование построителя для десериализации,
- копирует специфичную для поля конфигурацию из аннотированного класса в сгенерированный построитель (например,
@JsonIgnoreProperties
), и - выравнивает префикс Джексона, используемый в методах построения (например,
builder().withField(field)
противbuilder.field(field)
к настроенному на Ломбоке.
Ссылаясь на ответ Джозефа К. Штрауса, я нашел следующее решение.
Простые аннотации Ломбок, которые сработали для меня, выглядит следующим образом. Следующие аннотации дают вам неизменные классы с компоновщиком, которые Джексон может сериализовать и десериализовать.
@Data
@Setter(AccessLevel.NONE)
@Builder(toBuilder = true)
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Clazz {
private String field;
}
Я предпочитаю это решение, потому что оно не требует дополнительных аннотаций, специфичных для Джексона, и никаких дополнительных файлов, специфичных для lombok.
Еще одна альтернатива, которая гораздо менее многословна:
@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;
}
Самый простой способ настроить неизменяемый класс для Джексона - использовать аннотацию lombok: @Value
и @Jacksonized
:
@Jacksonized
@Builder
@Value
class Foo {
}
Я только что решил это следующим образом:
@Value
@Builder(setterPrefix = "with")
@JsonDeserialize(builder = Clazz.ClazzBuilder.class)
public class Clazz {
private String field;
}
В этом случае вы должны использовать такие методы построителя, как
withField(...)
, что является поведением по умолчанию, используемым Джексоном.
Попробуйте следующий набор аннотаций для вашего pojo:
@Value
@NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
@AllArgsConstructor
Ответ Томаса Фрича отлично работал с Spring Boot после добавления зависимости Jackson Dataformat в 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>