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


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разрешению ломбока для этого инструмента.
— Марк


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

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


-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
— Мухаммад Вакас Дилавар
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.