web-dev-qa-db-fra.com

Impossible que Jackson et Lombok travaillent ensemble

J'expérimente en combinant Jackson et Lombok. Ce sont mes cours:

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

}

Ce sont les fichiers JAR que j'ajoute à la classe:

Je le compile avec Netbeans (je ne pense pas que cela soit vraiment pertinent, mais je le rapporte quand même pour le rendre parfaitement et fidèlement reproductible). Les cinq fichiers JAR ci-dessus sont conservés dans un dossier appelé "lib" dans le dossier du projet (avec "src", "nbproject", "test" et "build"). Je les ai ajoutés à Netbeans via le bouton "Add JAR/Folder" dans les propriétés du projet et ils sont répertoriés dans l'ordre exact de la liste ci-dessus. Le projet est un projet de type "application Java" standard.

En outre, le projet Netbeans est configuré pour "ne PAS compiler sur save", "générer des informations de débogage", "signaler les API obsolètes", "suivre les dépendances Java", "Processus d'annotation activacte" et "Processus d'annotation activacte dans l'éditeur". Aucun processeur d'annotation ou option de traitement d'annotation n'est explicitement configuré dans Netbeans. En outre, l'option de ligne de commande "-Xlint:all" est transmise à la ligne de commande du compilateur et le compilateur s'exécute sur une machine virtuelle externe.

La version de mon javac est la 1.8.0_72 et la version de mon Java est la 1.8.0_72-b15. Mon Netbeans est 8.1.

Mon projet compile bien. Cependant, il lève une exception dans son exécution. L'exception ne semble pas être quelque chose qui semble facile ou évident réparable. Voici le résultat, y compris le 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 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

J'ai déjà essayé de taper au hasard avec les annotations @Value et @AllArgsConstructor, mais je ne pouvais pas l'améliorer.

J'ai fait une exception sur Google et trouvé un vieux rapport de bogue sur jackson , et un autre qui est ouvert, mais qui semble être lié à autre chose . Cependant, cela ne dit toujours rien sur ce qu'est ce bogue ni sur la façon de le résoudre. De plus, je ne trouvais rien d’utile à regarder ailleurs.

Étant donné que j'essaie de faire un usage très élémentaire de Lombok et de Jackson, il semble étrange de ne pouvoir trouver aucune information plus utile sur la manière de résoudre ce problème. Peut-être que j'ai raté quelque chose?

Autre que le simple fait de dire "ne pas utiliser lombok" ou "ne pas utiliser jackson", quelqu'un a-t-il une idée de la façon de résoudre ce problème?

36
Victor Stafusa

Si vous voulez immuable mais un POJO sérialisable json utilisant lombok et jackson . Utilisez jacksons une nouvelle annotation sur votre constructeur lomboks @JsonPOJOBuilder(withPrefix = "") J'ai essayé cette solution et cela fonctionne très bien . Exemple d'utilisation

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 {

    }
}

Si vous avez trop de classes avec @Builder et que vous ne voulez pas que l'annotation vide du code standard soit plébiscitée, vous pouvez remplacer l'intercepteur d'annotation pour avoir withPrefix vide

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

Et vous pouvez supprimer la classe de générateur vide avec l'annotation @JsonPOJOBuilder.

18
ganesan dharmalingam

J'ai eu exactement le même problème, "résolu" en ajoutant le paramètre suppressConstructorProperties = true (en utilisant votre exemple):

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

Apparemment, Jackson n'aime pas les annotations Java.beans.ConstructorProperties ajoutées aux constructeurs. Le paramètre suppressConstructorProperties = true indique à Lombok de ne pas l'ajouter (c'est le cas par défaut).

5
Donovan Muller

@AllArgsConstructor(suppressConstructorProperties = true) est obsolète. Définissez lombok.anyConstructor.suppressConstructorProperties=true ( https://projectlombok.org/features/configuration ) et changez l'annotation lombok de POJO de @Value à @Data + @NoArgsConstructor + @AllArgsConstructor fonctionne pour moi. 

5
yyy

Vous devez également avoir ce module . https://github.com/FasterXML/jackson-modules-Java8

puis activez le paramètre -parameters pour votre compilateur. 

<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

Vous pouvez obtenir que Jackson joue avec à peu près n'importe quoi si vous utilisez son modèle "mixin" . En gros, cela vous permet d'ajouter des annotations de Jackson à une classe existante sans modifier cette classe. Je suis plutôt enclin à le recommander ici plutôt qu'à une solution de Lombok, car cela résout un problème rencontré par Jackson avec une fonctionnalité de Jackson.

1
David Ehrmann

Pour moi, cela a fonctionné lorsque j'ai mis à jour la version de lombok avec: 'Org.projectlombok: lombok: 1.18.0'

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

En plus de la classe de données, il devrait être correctement configuré ObjectMapper. Dans ce cas, cela fonctionne bien avec une configuration ParameterNamesModule et en définissant la visibilité des méthodes Fields et Creator.

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

Ensuite, cela devrait fonctionner comme prévu.

0
Hector Delgado

J'ai annoté tous mes cours comme ceci:

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

Cela a fonctionné avec toutes les versions de Lombok et de Jackson pendant au moins quelques années.

Exemple:

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

Et ça y est . Lombok et Jackson jouent ensemble comme un charme.

0
where_

J'avais de la difficulté à faire en sorte que Lombok ne rajoute pas l'annotation ConstructorProperies, alors je suis allé dans l'autre sens et a empêché Jackson de regarder cette annotation.

Le coupable est JacksonAnnotationIntrospector.findCreatorAnnotation . Remarquer:

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

Remarquez également JacksonAnnotationIntrospector.setConstructorPropertiesImpliesCreator :

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

Donc, deux options, soit définir le MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES sur false ou créer un JacksonAnnotationIntrospector définir setConstructorPropertiesImpliesCreator sur false et définir ceci AnnotationIntrospector dans ObjectMapper via ObjectMapper.setAnnotationIntrospector .

Remarquez quelques choses, j'utilise Jackson 2.8.10 et dans cette version MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES n'existe pas. Je ne sais pas dans quelle version de Jackson il a été ajouté. Donc, si ce n’est pas le cas, utilisez le mécanisme JacksonAnnotationIntrospector.setConstructorPropertiesImpliesCreator.

0
John B