Не могу заставить Джексона и Ломбока работать вместе

97

Я экспериментирую в сочетании Джексона и Ломбока. Это мои занятия:

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-файлы, которые я добавляю в classpth:

Я компилирую его с помощью Netbeans (я не думаю, что это действительно актуально, но я все равно сообщаю об этом, чтобы сделать его идеально и точно воспроизводимым). Вышеупомянутые пять JAR хранятся в папке с именем " lib" внутри папки проекта (вместе с " src", " nbproject", " test" и " build"). Я добавил их в Netbeans с помощью кнопки « Добавить JAR / папку » в свойствах проекта, и они перечислены в точном порядке, как в списке выше. Это стандартный проект типа «Java-приложение».

Кроме того, проект Netbeans настроен на « НЕ компилировать при сохранении », « генерировать отладочную информацию », « сообщать об устаревших API », « отслеживать зависимости java », « обрабатывать аннотации activacte » и « обрабатывать аннотации activacte в редакторе ». Обработчик аннотаций или опция обработки аннотаций явно не настроены в 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аннотации, но я не мог сделать это лучше.

Я погуглил исключение и нашел старый отчет об ошибке на Джексоне и еще один открытый, но, похоже, связанный с чем-то другим . Однако это все еще ничего не говорит о том, что это за ошибка или как ее исправить. Кроме того, я не мог найти ничего полезного в поисках чего-либо еще.

Поскольку то, что я пытаюсь сделать, - это очень простое использование ломбока и джексона, кажется странным, что я не смог найти более полезной информации о том, как решить эту проблему. Может я что то упустил?

Есть ли у кого-нибудь идеи, кроме как просто сказать « не используйте ломбок » или « не используйте Джексона », как это решить?

Виктор Стафуса
источник

Ответы:

59

Если вам нужен неизменяемый, но сериализуемый 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
38

Неизменяемость + Ломбок + Джексон может быть достигнута следующим образом:

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аннотации?
Зон
8
Явное добавление @NoArgsConstructorпереопределяет конструктор, созданный @Valueаннотацией, поэтому вам нужно добавить дополнительный@AllArgsConstructor
Давио
1
На мой взгляд, лучшее решение, которое я нашел, без шаблона Builder. Благодарю.
Radouxca
16

Я попробовал несколько из вышеперечисленных, и все они были темпераментными. Что действительно сработало для меня, так это ответ, который я нашел здесь .

в корневой каталог вашего проекта добавьте файл 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 и Builder или RequiredArgsConstructor и пометьте все поля как окончательные
nekperu15739
какие-нибудь идеи о том, почему это не работает в моем случае? У меня есть приложение для весенней загрузки, и я поместил lombok.config в свой src, но ваше решение все равно не работает.
Thanos M
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.
Майкл Пифель
4

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

ггг
источник
14
Это изменяет класс, чтобы он стал изменяемым, что, я полагаю, не то, что хотел OP,
Амит Голдштейн
3

Из ответа Яна Рике

Начиная с 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
Это решает мою проблему! Спасибо огромное! Я застрял на весенней загрузке-интеграции и сериализации между JSON и POJO и получил ошибку: Can not deserialize instance of java.lang.String out of START_OBJECT token... И это исправило! Мне нужно иметь эту @JsonPropertyаннотацию для каждого параметра конструктора, и это дополнение к @AllArgsConstructorразрешению ломбока для этого инструмента.
Марк
2

Для меня это сработало, когда я обновил версию lombok до: 'org.projectlombok: lombok: 1.18.0'

фасцинация
источник
1

Вы можете заставить Джексона поиграть с чем угодно, если воспользуетесь его паттерном "миксин" . По сути, это дает вам возможность добавлять аннотации Джексона к существующему классу без фактического изменения этого класса. Я склоняюсь к тому, чтобы рекомендовать его здесь, а не решение Lombok, потому что это решает проблему, которую Джексон имеет с функцией Джексона, поэтому с большей вероятностью он будет работать в долгосрочной перспективе.

Дэвид Эрманн
источник
1

Все мои классы аннотированы следующим образом:

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

Он работал со всеми версиями Lombok и Jackson, по крайней мере, пару лет.

Пример:

@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 04
0
@JsonInclude(JsonInclude.Include.NON_NULL)
@Data
public class Person {
   String id;
   String first;
   String last;
}

В дополнение к классу данных необходимо правильно настроить ObjectMapper. В этом случае он работает нормально с конфигурацией ParameterNamesModule и настройкой видимости полей и методов создателя.

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

Тогда все должно работать как положено.

Гектор Дельгадо
источник
0

У меня были проблемы с тем, чтобы Lombok не добавлял 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

затем включите флаг -параметры для вашего компилятора.

<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();
   }
}

Конечно, вы также можете просто вручную добавить конструктор в себя.

пользователь3416682
источник
-1

У меня была другая проблема, и это было с логическими примитивными типами.

private boolean isAggregate;

В результате возникла следующая ошибка

Exception: Unrecognized field "isAggregate" (class 

Ламбок преобразуется isAggregateв isAggregate()геттер, aggregateвместо этого свойство внутренне превращается в ломбок isAggregate. Библиотеке Джексона это не нравится, и isAggregateвместо этого ей нужна собственность.

Я обновил примитивное логическое значение до Wrapper Boolean, чтобы обойти эту проблему. Есть и другие варианты, если вы имеете дело сboolean типами, , см. Ссылку ниже.

Sol:

private Boolean isAggregate;

ссылка: https://www.baeldung.com/lombok-getter-boolean

Зевс
источник
Вы пробовали использовать агрегат вместо isAggregate с примитивным типом? Я считаю, что это тоже может устранить ошибку. См. Также stackoverflow.com/a/42620096/6332074
Мухаммад Вакас Дилавар