Неизменный Ломбок аннотировал класс с Джексоном

Каков предпочтительный способ создания класса?

  • Неизменный
  • Может быть сериализовано / десериализовано с Джексоном
  • Человекочитаемый и с низким уровнем шаблона

Желательно, чтобы мне понравилось что-то подобное:

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

План POJO

Вот тест 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);
    }
}

Еще одна альтернатива, которая:

это добавить 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>
Другие вопросы по тегам