wake-up-neo.com

Ich kann Jackson und Lombok nicht zusammenarbeiten lassen

Ich experimentiere mit Jackson und Lombok. Das sind meine Klassen:

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([email protected]__(@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);
    }

}

Das sind die JARs, die ich in den classpth einfügt:

Ich kompiliere es mit Netbeans (ich glaube nicht, dass dies wirklich relevant ist, aber ich melde das trotzdem, um es perfekt und originalgetreu reproduzierbar zu machen). Die fünf obigen JARs befinden sich in einem Ordner namens "lib" im Projektordner (zusammen mit "src", "nbproject", "test" und "build"). Ich habe sie über die Schaltfläche "Add JAR/Folder" in den Projekteigenschaften zu Netbeans hinzugefügt. Sie werden in der genauen Reihenfolge wie in der Liste oben aufgeführt. Das Projekt ist ein Standardprojekt vom Typ "Java-Anwendung".

Darüber hinaus ist das Netbeans-Projekt so konfiguriert, dass "NICHT beim Speichern kompiliert werden", "Debugging-Informationen generieren", "veraltete APIs", "Java-Abhängigkeiten verfolgen", "activacte Annotationsverarbeitung" und "activacte Annotationsverarbeitung im Editor". In Netbeans ist keine Anmerkungsprozessor- oder Anmerkungsverarbeitungsoption explizit konfiguriert. In der Compiler-Befehlszeile wird auch die Befehlszeilenoption "-Xlint:all" übergeben, und der Compiler wird auf einer externen VM ausgeführt.

Meine Javac-Version ist 1.8.0_72 und meine Java-Version ist 1.8.0_72-b15. Mein Netbeans ist 8.1.

Mein Projekt kompiliert gut. Es gibt jedoch eine Ausnahme bei der Ausführung. Die Ausnahme scheint nichts zu sein, was leicht oder naheliegend fixierbar erscheint. Hier ist die Ausgabe inklusive Stacktrace:

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 [email protected]es(value=[x, z]), interface [email protected]son.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 [email protected]es(value=[x, z]), interface [email protected]son.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

Ich habe bereits versucht, mit den @Value- und @AllArgsConstructor-Anmerkungen zufällig zu stochern, aber ich konnte es nicht besser machen.

Ich google'd die Ausnahme und fand einen alten Fehlerbericht über Jackson und einen anderen, der offen ist, aber mit etwas anderem in Verbindung zu stehen scheint . Dies sagt jedoch immer noch nichts darüber, was dieser Fehler ist oder wie er behoben werden kann. Ich konnte auch nichts Nützliches finden, das woanders aussieht.

Da ich versuche, Lombok und Jackson ganz grundlegend zu verwenden, erscheint es mir merkwürdig, dass ich keine nützlicheren Informationen zur Lösung dieses Problems finden konnte. Vielleicht habe ich etwas verpasst?

Abgesehen davon, dass Sie nur sagen "benutze kein Lombok" oder "benutze kein Jackson", hat jemand eine Idee, wie Sie das lösen können?

36
Victor Stafusa

Wenn Sie unveränderlich, aber einen json-serialisierbaren POJO verwenden möchten, der Lombok und Jackson verwendet, verwenden Sie jacksons neue Anmerkungen zu Ihrem Lomboks-Builder @JsonPOJOBuilder(withPrefix = "") Ich habe diese Lösung ausprobiert, und es funktioniert sehr gut

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 {

    }
}

Wenn Sie zu viele Klassen mit @Builder haben und nicht möchten, dass der Boilerplate-Code leer ist, können Sie den Annotations-Interceptor mit withPrefix überschreiben.

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", "");
        }
    });

Sie können die leere Builder-Klasse mit der @JsonPOJOBuilder-Annotation entfernen.

18

Ich hatte genau das gleiche Problem, "gelöst" es durch Hinzufügen des Parameters suppressConstructorProperties = true (anhand Ihres Beispiels):

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

Jackson mag die an Konstruktoren hinzugefügte Annotation Java.beans.ConstructorProperties offenbar nicht. Der suppressConstructorProperties = true-Parameter weist an, dass Lombok es nicht hinzufügt (standardmäßig).

5
Donovan Muller

@AllArgsConstructor(suppressConstructorProperties = true) ist veraltet. Definieren Sie lombok.anyConstructor.suppressConstructorProperties=true ( https://projectlombok.org/features/configuration ) und ändern Sie die Lombok-Anmerkung von POJO von @Value in @Data + @NoArgsConstructor + @AllArgsConstructor

5
yyy

Unveränderlich + Lombok + Jackson kann auf folgende Weise erreicht werden:

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);
    }
}
5
Alex Efimov

Ich habe einige der oben genannten Dinge ausprobiert und sie waren alle temperamentvoll. Was für mich wirklich funktioniert hat, ist die Antwort, die ich gefunden habe hier .

fügen Sie im Stammverzeichnis Ihres Projekts eine Datei hinzu (falls noch nicht geschehen).

lombok.config

und innen einfügen

lombok.anyConstructor.addConstructorProperties=true

Dann können Sie Ihre Pojos wie folgt definieren:

@Data
@AllArgsConstructor
public class MyPojo {

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

Sie müssen dieses Modul ebenfalls haben . https://github.com/FasterXML/jackson-modules-Java8

aktivieren Sie dann das Parameter -Parameter-Flag für Ihren Compiler. 

<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>
1
Matt Broekhuis

Sie können Jackson dazu bringen, mit fast allem zu spielen, wenn Sie das "Mixin" -Muster verwenden. Im Grunde gibt es eine Möglichkeit, Jackson-Anmerkungen zu einer vorhandenen Klasse hinzuzufügen, ohne diese Klasse tatsächlich zu ändern. Ich neige dazu, es hier eher zu empfehlen als eine Lombok-Lösung, da dies ein Problem löst, das Jackson mit einer Jackson-Funktion hat, und daher eher langfristig zu arbeiten ist.

1
David Ehrmann

Für mich hat es funktioniert, wenn ich die Lombok-Version aktualisiert habe:.

1
fascynacja

Aus Jan Riekes Antwort

Seit lombok 1.18.4 können Sie konfigurieren, welche Anmerkungen in die Konstruktorparameter kopiert werden. Fügen Sie dies in Ihr lombok.config:

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

Dann füge einfach @JsonProperty zu Ihren Feldern:

...

Sie benötigen in jedem Feld eine @JsonProperty, auch wenn der Name übereinstimmt. Dies ist jedoch eine gute Vorgehensweise. Sie können damit auch Ihre Felder in das öffentliche Finale versetzen, was ich Gettern vorziehe.

@ToString
@EqualsAndHashCode
@Wither
@AllArgsConstructor([email protected]__(@JsonCreator))
public class TestFoo {
    @JsonProperty("xoom")
    private final String x;
    @JsonProperty("z")
    private final int z;
}
0
Mingwei Samuel
@JsonInclude(JsonInclude.Include.NON_NULL)
@Data
public class Person {
   String id;
   String first;
   String last;
}

Zusätzlich zur Datenklasse sollte der ObjectMapper korrekt konfiguriert sein. In diesem Fall funktioniert es mit einer ParameterNamesModule-Konfiguration und der Einstellung der Sichtbarkeit von Fields- und Creator-Methoden

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

Dann sollte es wie erwartet funktionieren.

0
Hector Delgado

Ich hatte auch einen Moment damit zu kämpfen. Aber wenn ich mir die Dokumentation ansehe hier Ich kann sehen, dass der Annotationsparameter onConstructor als experimentell angesehen wird und auf meinem System nicht gut unterstützt wird IDE (STS 4). Laut Jackson In der Dokumentation werden private Mitglieder standardmäßig nicht (de) serialisiert. Es gibt schnelle Möglichkeiten, dies zu beheben.

Fügen Sie die Annotation JsonAutoDetect hinzu und stellen Sie sie entsprechend ein, um geschützte/private Mitglieder zu erkennen. Dies ist praktisch für DTOs

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

Fügen Sie eine Factory-Funktion mit @JsonCreator-Annotation hinzu. Dies funktioniert am besten, wenn Sie eine Objektvalidierung oder zusätzliche Transformationen benötigen.

public class SomeClass {

   // some code here

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

Natürlich können Sie den Konstruktor auch manuell in sich selbst hinzufügen.

0
user3416682

Ich habe alle meine Klassen so kommentiert:

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

Es funktionierte mindestens ein paar Jahre mit allen Lombok- und Jackson-Versionen.

Beispiel:

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

Und das ist es ... Lombok und Jackson spielen zusammen wie ein Zauber.

0
where_

Ich hatte Probleme damit, Lombok dazu zu bringen, die ConstructorProperies-Anmerkung nicht hinzuzufügen, also ging er den anderen Weg und deaktivierte Jackson von der Anzeige dieser Anmerkung.

Der Täter ist JacksonAnnotationIntrospector.findCreatorAnnotation . Beachten:

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

Beachten Sie auch JacksonAnnotationIntrospector.setConstructorPropertiesImpliesCreator :

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

Also zwei Optionen, entweder MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES auf false setzen oder eine JacksonAnnotationIntrospector setzen, setConstructorPropertiesImpliesCreator auf false setzen und diese AnnotationIntrospector über ObjectMapper.setAnnotationIntrospector in die ObjectMapper setzen.

Beachten Sie ein paar Dinge, ich verwende Jackson 2.8.10 und in dieser Version existiert MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES nicht. Ich bin nicht sicher, in welcher Version von Jackson sie hinzugefügt wurde. Wenn dies nicht der Fall ist, verwenden Sie den JacksonAnnotationIntrospector.setConstructorPropertiesImpliesCreator-Mechanismus.

0
John B

Versuchen Sie, einen @NoArgsConstructor hinzuzufügen

0
Victor Grazi