Не можу змусити Джексона та Ломбока працювати разом


91

Я експериментую у поєднанні Джексона та Ломбока. Це мої заняття:

package testelombok;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Value;
import lombok.experimental.Wither;

@Value
@Wither
@AllArgsConstructor(onConstructor=@__(@JsonCreator))
public class TestFoo {
    @JsonProperty("xoom")
    private String x;
    private int z;
}
package testelombok;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.xebia.jacksonlombok.JacksonLombokAnnotationIntrospector;
import java.io.IOException;

public class TestLombok {

    public static void main(String[] args) throws IOException {
        TestFoo tf = new TestFoo("a", 5);
        System.out.println(tf.withX("b"));
        ObjectMapper om = new ObjectMapper().setAnnotationIntrospector(new JacksonLombokAnnotationIntrospector());
        System.out.println(om.writeValueAsString(tf));
        TestFoo tf2 = om.readValue(om.writeValueAsString(tf), TestFoo.class);
        System.out.println(tf2);
    }

}

Це ті JAR, які я додаю в клас:

Я складаю це з Netbeans (я не думаю, що це дійсно актуально, але я все одно повідомляю про це, щоб зробити це ідеально і достовірно відтворюваним). П'ять наведених вище JAR зберігаються у папці " lib" всередині папки проекту (разом із " src", " nbproject", " test" та " build"). Я додав їх до Netbeans через " Додати JAR / папку " у властивостях проекту, і вони перераховані в точному порядку, як у списку вище. Проект є стандартним проектом типу "додаток Java".

Крім того, проект Netbeans налаштований на те, що " НЕ компілювати при збереженні ", " генерувати інформацію про налагодження ", " звітувати про застарілі API ", " відстежувати залежності Java ", " обробляти анотації активації " та " обробляти активації анотацій у редакторі ". Жоден процесор анотацій або варіант обробки анотацій явно не налаштовані в Netbeans. Також параметр -Xlint:allкомандного рядка передається в командному рядку компілятора, і компілятор працює на зовнішній ВМ.

Версія мого javac - 1.8.0_72, а моя версія java - 1.8.0_72-b15. Мій Netbeans - 8,1.

Мій проект складений чудово. Однак у його виконанні виникає виняток. Винятком, здається, не є те, що виглядає легко чи очевидно, що можна виправити. Ось вихідні дані, включаючи стек:

TestFoo(x=b, z=5)
{"z":5,"xoom":"a"}
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Argument #0 of constructor [constructor for testelombok.TestFoo, annotations: {interface java.beans.ConstructorProperties=@java.beans.ConstructorProperties(value=[x, z]), interface com.fasterxml.jackson.annotation.JsonCreator=@com.fasterxml.jackson.annotation.JsonCreator(mode=DEFAULT)}] has no property name annotation; must have name when multiple-parameter constructor annotated as Creator
 at [Source: {"z":5,"xoom":"a"}; line: 1, column: 1]
    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:296)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:269)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
    at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
    at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:475)
    at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:3890)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3785)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2833)
    at testelombok.TestLombok.main(TestLombok.java:14)
Caused by: java.lang.IllegalArgumentException: Argument #0 of constructor [constructor for testelombok.TestFoo, annotations: {interface java.beans.ConstructorProperties=@java.beans.ConstructorProperties(value=[x, z]), interface com.fasterxml.jackson.annotation.JsonCreator=@com.fasterxml.jackson.annotation.JsonCreator(mode=DEFAULT)}] has no property name annotation; must have name when multiple-parameter constructor annotated as Creator
    at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._addDeserializerConstructors(BasicDeserializerFactory.java:511)
    at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._constructDefaultValueInstantiator(BasicDeserializerFactory.java:323)
    at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory.findValueInstantiator(BasicDeserializerFactory.java:253)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:219)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:141)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:406)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:352)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:264)
    ... 7 more

Я вже намагався навмання тикати з анотаціями @Valueта @AllArgsConstructor, але не зміг зробити це краще.

Я погуглив виняток і знайшов старий звіт про помилки на Джексоні та ще один, який є відкритим, але, схоже, пов’язаний з чимось іншим . Однак це все ще нічого не говорить про те, що це за помилка або як її виправити. Крім того, я не міг знайти нічого корисного, дивлячись десь ще.

Оскільки те, що я намагаюся зробити, - це дуже елементарне використання як lombok, так і jackson, здається дивним, що я не міг знайти більше корисної інформації про те, як вирішити цю проблему. Може, я щось пропустив?

Інші , ніж просто сказати « не використовувати Ломбок » або « не використовувати Джексоном », зробіть хто - небудь має ні найменшого уявлення про те , як вирішити цю проблему?

Відповіді:


58

Якщо ви хочете незмінний, але json серіалізується POJO з використанням lombok та jackson. Використовуйте нову анотацію jacksons у вашому конструкторі lomboks. @JsonPOJOBuilder(withPrefix = "") Я спробував це рішення, і воно працює дуже добре. Зразок використання

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import lombok.Builder;
import lombok.Value;

@JsonDeserialize(builder = Detail.DetailBuilder.class)
@Value
@Builder
public class Detail {

    private String url;
    private String userName;
    private String password;
    private String scope;

    @JsonPOJOBuilder(withPrefix = "")
    public static class DetailBuilder {

    }
}

Якщо у вас занадто багато класів, @Builderі ви не хочете, щоб типовий код порожньої анотації, ви можете замінити перехоплювач анотацій, щоб мати порожнюwithPrefix

mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {
        @Override
        public JsonPOJOBuilder.Value findPOJOBuilderConfig(AnnotatedClass ac) {
            if (ac.hasAnnotation(JsonPOJOBuilder.class)) {//If no annotation present use default as empty prefix
                return super.findPOJOBuilderConfig(ac);
            }
            return new JsonPOJOBuilder.Value("build", "");
        }
    });

А ви можете видалити порожній клас будівельника з @JsonPOJOBuilderанотацією.


Посилання на рішення вітається, але будь ласка, переконайтесь, що ваша відповідь корисна без нього: додайте контекст навколо посилання, щоб ваші однодумці мали певне уявлення, що це таке і чому воно є, а потім цитуйте найбільш релевантну частину сторінки, яку ви “ повторне посилання на випадок, якщо цільова сторінка буде недоступна. Відповіді, які є лише кількома посиланнями, можуть бути видалені.
geisterfurz007

Це рішення зберігає незмінність, працюючи над проблемами десеріалізації, заснованими на конструкторі Джексона (багатоконтурний конструктор вимагає @JsonProperty для кожного аргументу конструктора, а не намагається щось розумне). (Я пробував onConstructor _ = {@ JsonCreator} без удачі)
JeeBee

35

Незмінних + Ломбок + Джексон можна досягти наступним чином:

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Value;

@Value
@NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
@AllArgsConstructor
public class LocationDto {

    double longitude;
    double latitude;
}

class ImmutableWithLombok {

    public static void main(String[] args) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();

        String stringJsonRepresentation = objectMapper.writeValueAsString(new LocationDto(22.11, 33.33));
        System.out.println(stringJsonRepresentation);

        LocationDto locationDto = objectMapper.readValue(stringJsonRepresentation, LocationDto.class);
        System.out.println(locationDto);
    }
}

4
Це AllArgsConstructorвже не частина @Valueанотації?
Zon

8
Додавання явного @NoArgsConstructorзамінює конструктор, який генерується @Valueанотацією, тому вам доведеться додати додаткове@AllArgsConstructor
Давіо

1
Найкраще рішення, яке я знайшов - на мій погляд - без шаблону Builder. Дякую.
Радушка

15

Я спробував декілька з перерахованих вище, і всі вони були темпераментними. Що справді спрацювало для мене, так це відповідь, яку я знайшов тут .

у кореневий каталог вашого проекту додайте файл lombok.config (якщо ви цього ще не зробили)

lombok.config

і всередину вставте це

lombok.anyConstructor.addConstructorProperties=true

Тоді ви можете визначити свої pojos наступним чином:

@Data
@AllArgsConstructor
public class MyPojo {

    @JsonProperty("Description")
    private String description;
    @JsonProperty("ErrorCode")
    private String errorCode;
}

2
Але хіба це не змінюється?
vphilipnyc

щоб бути незмінним, просто змініть дані за допомогою Getter and Builder або RequiredArgsConstructor і позначте всі поля як остаточні
nekperu15739

8

У мене була точно така ж проблема, "вирішена", додавши suppressConstructorProperties = trueпараметр (на вашому прикладі):

@Value
@Wither
@AllArgsConstructor(suppressConstructorProperties = true)
public class TestFoo {
    @JsonProperty("xoom")
    private String x;
    private int z;
}

Джексону, мабуть, не подобається java.beans.ConstructorProperties анотація, додана до конструкторів. suppressConstructorProperties = trueПараметр говорить Ломбок не додав (це відбувається за замовчуванням).


7
suppressConstructorPropertiesзараз застаріло :-(
lilalinux

3
Що не є проблемою, оскільки новий за замовчуванням також false.
Michael Piefel

4

@AllArgsConstructor(suppressConstructorProperties = true)застаріло. Визначте lombok.anyConstructor.suppressConstructorProperties=true( https://projectlombok.org/features/configuration ) і змініть анотацію ломбока POJO @Valueна @Data+ @NoArgsConstructor+ @AllArgsConstructorпрацює для мене.


14
Це змінює клас на мінливий, що, на мою думку, не те, що хотів ОП
Аміт Гольдштейн

2

Для мене це спрацювало, коли я оновив версію lombok до: 'org.projectlombok: lombok: 1.18.0'


2

З відповіді Яна Ріке

З версії lombok 1.18.4 ви можете налаштувати, які анотації копіюються до параметрів конструктора. Вставте це у свій lombok.config:

lombok.copyableAnnotations += com.fasterxml.jackson.annotation.JsonProperty

Тоді просто додайте @JsonPropertyдо своїх полів:

...

Вам знадобиться @JsonProperty у кожному полі, навіть якщо ім'я збігається, але це хороша практика, яку слід дотримуватися в будь-якому випадку. Ви також можете встановити для своїх полів загальнодоступний фінал, використовуючи це, що я віддаю перевагу над геттерами.

@ToString
@EqualsAndHashCode
@Wither
@AllArgsConstructor(onConstructor=@__(@JsonCreator))
public class TestFoo {
    @JsonProperty("xoom")
    public final String x;
    @JsonProperty("z")
    public final int z;
}

Він також повинен працювати з геттерами (+ сеттерами).


1
Це вирішує моє питання! Дуже дякую! Я застряг на інтеграції spring-boot та серіалізації між JSON та POJO і отримав помилку: Can not deserialize instance of java.lang.String out of START_OBJECT token... І це виправило! Мені потрібно мати цю @JsonPropertyанотацію для кожного параметра конструктора, а також додаток до @AllArgsConstructorдозволу lombok інструментувати це.
Позначте

1

Ви можете змусити Джексона пограти практично з будь-чим, якщо скористатися його шаблоном "mixin" . По суті, це дає вам спосіб додавати анотації Джексона до існуючого класу, фактично не змінюючи цей клас. Я схиляюся до того, щоб рекомендувати його тут, а не рішення Ломбока, оскільки це вирішує проблему, яку має Джексон із функцією Джексона, тому, швидше за все, вона буде працювати довгостроково.


1

Я всі свої класи анотував так:

@JsonAutoDetect(fieldVisibility = Visibility.ANY)
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
@Data
@Accessors(fluent = true)
@NoArgsConstructor
@AllArgsConstructor

Він працював з усіма версіями Ломбока та Джексона, принаймні, пару років.

Приклад:

@JsonAutoDetect(fieldVisibility = Visibility.ANY)
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
@Data
@Accessors(fluent = true)
@NoArgsConstructor
@AllArgsConstructor
public class Person {
    String id;
    String first;
    String last;
}

І це все. Ломбок і Джексон грають разом, як шарм.


9
Це мінливий клас.
ŁukaszG

0
@JsonInclude(JsonInclude.Include.NON_NULL)
@Data
public class Person {
   String id;
   String first;
   String last;
}

Додатково до класу даних слід правильно налаштувати ObjectMapper. У цьому випадку це нормально працює з конфігурацією ParameterNamesModule та налаштуванням видимості полів та методів Creator

    om.registerModule(new ParameterNamesModule());
    om.setVisibility(FIELD, JsonAutoDetect.Visibility.ANY);
    om.setVisibility(CREATOR, JsonAutoDetect.Visibility.ANY);

Тоді це має працювати, як очікувалося.


0

У мене були проблеми з тим, щоб Ломбок не додав ConstructorProperiesанотацію, тому пішов іншим шляхом і відключив Джексона переглядати цю анотацію.

Винуватець - JacksonAnnotationIntrospector.findCreatorAnnotation . Примітка:

if (_cfgConstructorPropertiesImpliesCreator
            && config.isEnabled(MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES)

Також зверніть увагу на JacksonAnnotationIntrospector.setConstructorPropertiesImpliesCreator :

public JacksonAnnotationIntrospector setConstructorPropertiesImpliesCreator(boolean b)
{
    _cfgConstructorPropertiesImpliesCreator = b;
    return this;
}

Таким чином , два варіанти, або встановити MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIESбрехня або створити JacksonAnnotationIntrospectorнабір setConstructorPropertiesImpliesCreatorдля falseі встановити це AnnotationIntrospectorв ObjectMapperчерез ObjectMapper.setAnnotationIntrospector .

Зверніть увагу на кілька речей, я використовую Jackson 2.8.10 і в цій версії MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIESне існує. Я не впевнений, у якій версії Джексона він був доданий. Тож якщо його там немає, скористайтеся JacksonAnnotationIntrospector.setConstructorPropertiesImpliesCreatorмеханізмом.


0

Вам також потрібно мати цей модуль. https://github.com/FasterXML/jackson-modules-java8

потім увімкніть прапор -parameters для вашого компілятора.

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                    <compilerArgs>
                        <arg>-parameters</arg>
                    </compilerArgs>
                </configuration>
            </plugin>

0

Я боровся з цим і якусь мить. Але, переглядаючи документацію тут, я бачу, що параметр анотації onConstructor вважається експериментальним і погано підтримується в моїй IDE (STS 4). Згідно з документацією Джексона, приватні учасники не (де) серіалізовані за замовчуванням. Існують швидкі способи вирішення цієї проблеми.

Додайте анотацію JsonAutoDetect та встановіть її належним чином для виявлення захищених / приватних членів. Це зручно для DTO

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class SomeClass

Додайте заводську функцію з анотацією @JsonCreator, це найкраще працює, якщо вам потрібна перевірка об’єкта або додаткові перетворення.

public class SomeClass {

   // some code here

   @JsonCreator
   public static SomeClass factory(/* params here dressing them in @JsonProperty annotations*/) {
      return new SomeClass();
   }
}

Звичайно, ви також можете просто додати конструктор вручну.


-1

У мене була інша проблема, і це було з логічними примітивними типами.

private boolean isAggregate;

В результаті виникла наступна помилка

Exception: Unrecognized field "isAggregate" (class 

Lambok перетворюється isAggregateна isAggregate()як на геттера, що робить властивість внутрішньо на lombok як aggregateнатомість isAggregate. Бібліотеці Джексона це не подобається, і вона потребує isAggregateвласності.

Для оновлення цієї проблеми я оновив примітивний логічний формат до Wrapper Boolean. Є інші варіанти для вас, якщо ви маєте справуboolean типами, див. Посилання нижче.

Соль:

private Boolean isAggregate;

посилання: https://www.baeldung.com/lombok-getter-boolean


Ви намагалися використовувати агрегат замість isAggregate з примітивним типом? Я вважаю, це також може усунути помилку. Також перегляньте це stackoverflow.com/a/42620096/6332074
Мухаммад Вакас Ділавар,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.