Как использовать Джексона для десериализации внешнего класса Lombok Builder - PullRequest
0 голосов
/ 24 января 2019

У меня есть сторонний конструктор Lombok POJO, который я не могу изменить, и который я хочу сериализовать, используя Джексона. Примечательно, что не имеет NoArgsConstructor.

@Data
@Builder
public class ExternalClass {
   private String name;
   private String data; 
   // etc.
} 

На первый взгляд это может показаться простым, но на практике это невероятно расстраивает, поскольку каждому возможному варианту противодействует отдельное осложнение. По сути, у меня проблемы с получением внешнего Lombok builder для работы с Джексоном mixin .

Lombok производит плавные сеттеры стиля .name(String name), в то время как встроенный десериализатор Джексона ожидает .withName(String name). В документации Lombok и других рецептах, таких как здесь , предлагается использовать @JsonDeserialize(builder=ExternalClass.ExternalClassBuilder.class) в сочетании с @JsonPOJOBuilder(withPrefix="") в предварительно объявленном конструкторе внутренних заглушек. Но это невозможно, потому что класс Lombok находится во внешней библиотеке.

Применение этих аннотаций к миксину не имеет никакого эффекта.

@JsonDeserialize(ExternalClass.ExternalClassBuilder.class)
public abstract class ExternalClassMixin {
   @JsonPOJOBuilder(withPrefix="")
   public static ExternalClassBuilder {
   }
} 

Единственный подход, который я нашел, который работает, - это использовать пакетный доступ AllArgsConstructor, созданный @Builder, и заполнить миксин следующим конструктором

public abstract class ExternalClassMixin {
   @JsonCreator public ExternalClassMixin(
      @JsonProperty("name") String name,
      @JsonProperty("data") String data,
      // etc.
  ) {} 
} 

Это, очевидно, нежелательно, поскольку требует явной итерации и жесткого кодирования каждого свойства класса, что делает миксины хрупкими к любым изменениям во внешнем POJO.

У меня вопрос: есть ли надежный и поддерживаемый способ сериализации этого внешнего класса построителя с использованием Джексона без его изменения, с использованием миксина или, возможно, полноценного десериализатора?

Обновление

Я реализовал отличный ответ @ jan-rieke, включая предложение использовать рефлексию для поиска внутреннего класса строителей.

...
public Class<?> findPOJOBuilder(AnnotatedClass ac) {
   Class<?> innerBuilder;
   try {
      innerBuilder = Class.forName(ac.getName()+"$"+ac.getRawType().getSimpleName()+"Builder");
      log.info("Builder found: {}", ac.getName());
      return innerBuilder;
   } catch( ClassNotFoundException e ) {
      return super.findPOJOBuilder(ac);
   }
}

1 Ответ

0 голосов
/ 25 января 2019

Вы можете настроить ObjectMapper следующим образом:

    ObjectMapper mapper = new ObjectMapper();
    mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {
        @Override
        public Class<?> findPOJOBuilder(AnnotatedClass ac) {
            if (ExternalClass.class.equals(ac.getRawType())) {
                return ExternalClass.ExternalClassBuilder.class;
            }
            return super.findPOJOBuilder(ac);
        }

        @Override
        public Value findPOJOBuilderConfig(AnnotatedClass ac) {
            if (ac.hasAnnotation(JsonPOJOBuilder.class)) {
                return super.findPOJOBuilderConfig(ac);
            }
            return new JsonPOJOBuilder.Value("build", "");
        }
    });

Это будет

  • явно настроить, чтобы при десериализации для ExternalClass использовался ее построитель, а
  • установить префикс по умолчанию для методов установки компоновщика на "" (кроме случаев, когда присутствует аннотация @JsonPOJOBuilder).

Если вы не хотите явно перечислять все внешние классы в findPOJOBuilder(), вы, конечно, можете программно заглянуть в класс, чтобы проверить, имеет ли он внутренний класс, который выглядит как конструктор.

...