Non riesco a far lavorare Jackson e Lombok insieme


97

Sto sperimentando combinando Jackson e Lombok. Queste sono le mie classi:

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

}

Questi sono i JAR che sto aggiungendo alla classe:

Lo sto compilando con Netbeans (non credo che questo sia realmente rilevante, ma lo riporto comunque per renderlo perfettamente e fedelmente riproducibile). I cinque JAR sopra sono conservati in una cartella chiamata " lib" all'interno della cartella del progetto (insieme a " src", " nbproject" "," test"e" build"). Li ho aggiunti a Netbeans tramite il pulsante " Aggiungi JAR / Cartella " nelle proprietà del progetto e sono elencati nell'ordine esatto dell'elenco sopra. Il progetto è un progetto di tipo "applicazione Java" standard.

Inoltre, il progetto Netbeans è configurato per " NON compilare al salvataggio ", " generare informazioni di debug ", " segnalare API deprecate ", " tenere traccia delle dipendenze Java ", " processo di annotazione activacte " e " processo di annotazione activacte nell'editor ". Nessun processore di annotazioni o opzione di elaborazione delle annotazioni è esplicitamente configurato in Netbeans. Inoltre, " -Xlint:all" l'opzione della riga di comando viene passata nella riga di comando del compilatore e il compilatore viene eseguito su una VM esterna.

La versione del mio javac è 1.8.0_72 e la versione del mio java è 1.8.0_72-b15. Il mio Netbeans è 8.1.

Il mio progetto si compila bene. Tuttavia, genera un'eccezione nella sua esecuzione. L'eccezione non sembra essere qualcosa che sembra facilmente o ovvio risolvibile. Ecco l'output, incluso lo 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

Ho già provato a colpire a caso con le annotazioni @Valuee @AllArgsConstructor, ma non sono riuscito a migliorarlo.

Ho cercato su Google l'eccezione e ho trovato una vecchia segnalazione di bug su Jackson e un'altra aperta, ma sembra essere correlata a qualcos'altro . Tuttavia, questo ancora non dice nulla su cosa sia questo bug o su come risolverlo. Inoltre, non sono riuscito a trovare nulla di utile cercando che altrove.

Dal momento che quello che sto cercando di fare è un utilizzo molto basilare sia di lombok che di jackson, sembra strano che non sia riuscito a trovare ulteriori informazioni utili su come risolvere questo problema. Forse mi sono perso qualcosa?

Oltre a dire " non usare lombok " o " non usare jackson ", qualcuno ha idea di come risolverlo?

Risposte:


59

Se vuoi immutabile ma un POJO serializzabile json usando lombok e jackson. Usa la nuova annotazione di jacksons sul tuo lomboks builder. @JsonPOJOBuilder(withPrefix = "") Ho provato questa soluzione e funziona molto bene. Utilizzo del campione

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 {

    }
}

Se hai troppe classi con @Buildere non vuoi che l'annotazione vuota del codice boilerplate puoi sovrascrivere l'intercettatore di annotazioni per averewithPrefix

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

E puoi rimuovere la classe builder vuota con @JsonPOJOBuilderannotazione.


Un collegamento a una soluzione è il benvenuto, ma assicurati che la tua risposta sia utile senza di essa: aggiungi un contesto attorno al collegamento in modo che i tuoi colleghi utenti abbiano un'idea di cosa sia e perché sia ​​lì, quindi cita la parte più pertinente della pagina che tu " re collegamento a nel caso in cui la pagina di destinazione non è disponibile. Le risposte che sono poco più di un collegamento possono essere eliminate.
geisterfurz007

Questa soluzione mantiene l'immutabilità mentre lavora intorno ai problemi di deserializzazione basati sul costruttore morto di cervello di Jackson (il costruttore multi-campo richiede @JsonProperty su ogni argomento del costruttore, piuttosto che provare qualcosa di intelligente). (Avevo provato onConstructor _ = {@ JsonCreator} senza fortuna)
JeeBee

38

Immutable + Lombok + Jackson può essere ottenuto nel modo seguente:

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
Non fa AllArgsConstructorgià parte @Valuedell'annotazione?
Zona

8
L'aggiunta di un esplicito @NoArgsConstructorsovrascrive il costruttore generato @Valuedall'annotazione, quindi devi aggiungere l'extra@AllArgsConstructor
Davio

1
La migliore soluzione che ho trovato - a mio parere - senza pattern Builder. Grazie.
Radouxca

16

Ho provato molti dei precedenti ed erano tutti capricciosi. Quello che ha funzionato davvero per me è la risposta che ho trovato qui .

nella directory principale del tuo progetto aggiungi un file lombok.config (se non l'hai già fatto)

lombok.config

e dentro incolla questo

lombok.anyConstructor.addConstructorProperties=true

Quindi puoi definire i tuoi pojo come segue:

@Data
@AllArgsConstructor
public class MyPojo {

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

2
Ma non è mutabile?
vphilipnyc

per essere immutabile, basta modificare i dati di Getter e Builder o RequiredArgsConstructor e contrassegnare tutti i campi come finali
nekperu15739

qualche idea sul perché questo non funziona nel mio caso? Ho un'applicazione di avvio primaverile e ho inserito lombok.config nel mio src ma la tua soluzione non funziona ancora.
Thanos M

8

Ho avuto esattamente lo stesso problema, "risolto" aggiungendo il suppressConstructorProperties = trueparametro (usando il tuo esempio):

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

A quanto pare a Jackson non piace l' java.beans.ConstructorProperties annotazione aggiunta ai costruttori. Il suppressConstructorProperties = trueparametro dice a Lombok di non aggiungerlo (lo fa per impostazione predefinita).


7
suppressConstructorPropertiesè ora deprecato :-(
lilalinux

3
Il che non è un problema, poiché lo è anche il nuovo predefinito false.
Michael Piefel

4

@AllArgsConstructor(suppressConstructorProperties = true)è deprecato. Definire lombok.anyConstructor.suppressConstructorProperties=true( https://projectlombok.org/features/configuration ) e modificare l'annotazione lombok di POJO da @Valuea @Data+ @NoArgsConstructor+ @AllArgsConstructorfunziona per me.


14
Questo cambia la classe in modo che sia mutabile, che presumo non sia quello che voleva OP
Amit Goldstein

3

Dalla risposta di Jan Rieke

A partire da lombok 1.18.4, puoi configurare quali annotazioni vengono copiate nei parametri del costruttore. Inseriscilo nel tuo lombok.config:

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

Quindi aggiungi @JsonPropertyai tuoi campi:

...

Avrai bisogno di una @JsonProperty in ogni campo anche se il nome corrisponde, ma è comunque una buona pratica da seguire. Puoi anche impostare i tuoi campi su public final usando questo, che preferisco rispetto ai getter.

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

Dovrebbe funzionare anche con getter (+ setters).


1
Questo risolve il mio problema! Grazie mille! Sono rimasto bloccato sull'integrazione e la serializzazione dello spring-boot tra JSON e POJO e ho ricevuto l'errore: Can not deserialize instance of java.lang.String out of START_OBJECT token... E questo lo ha risolto! Devo avere quell'annotazione @JsonPropertyper ogni parametro del costruttore e @AllArgsConstructorquell'aggiunta al permesso a lombok di strumentarlo.
Mark

2

Per me ha funzionato quando ho aggiornato la versione di lombok a: 'org.projectlombok: lombok: 1.18.0'


1

Puoi convincere Jackson a suonare con qualsiasi cosa se usi il suo schema di "mixaggio" . Fondamentalmente, ti dà un modo per aggiungere annotazioni di Jackson su una classe esistente senza effettivamente modificare quella classe. Mi propongo di consigliarlo qui piuttosto che una soluzione Lombok perché questo risolve un problema che Jackson sta avendo con una caratteristica di Jackson, quindi è più probabile che funzioni a lungo termine.


1

Ho tutte le mie classi annotate in questo modo:

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

Ha funzionato con tutte le versioni di Lombok e Jackson per almeno un paio d'anni.

Esempio:

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

E questo è tutto. Lombok e Jackson giocano insieme come un incantesimo.


9
Questa è una classe mutevole.
ŁukaszG

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

Oltre alla Data Class, dovrebbe essere configurato correttamente ObjectMapper. In questo caso, funziona correttamente con una configurazione ParameterNamesModule e l'impostazione della visibilità dei campi e dei metodi del creatore

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

Quindi dovrebbe funzionare come previsto.


0

Avevo problemi con Lombok a non aggiungere l' ConstructorProperiesannotazione, quindi sono andato dall'altra parte e ho impedito a Jackson di guardare quell'annotazione.

Il colpevole è JacksonAnnotationIntrospector.findCreatorAnnotation . Avviso:

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

Notare anche JacksonAnnotationIntrospector.setConstructorPropertiesImpliesCreator :

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

Quindi due opzioni, impostare il MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIESsu false o creare un JacksonAnnotationIntrospectorset setConstructorPropertiesImpliesCreatorsu falsee impostarlo AnnotationIntrospectorin ObjectMappervia ObjectMapper.setAnnotationIntrospector .

Notare un paio di cose, sto usando Jackson 2.8.10 e in quella versione MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIESnon esiste. Non sono sicuro in quale versione di Jackson sia stata aggiunta. Quindi, se non è presente, usa il JacksonAnnotationIntrospector.setConstructorPropertiesImpliesCreatormeccanismo.


0

Devi avere anche questo modulo. https://github.com/FasterXML/jackson-modules-java8

quindi attiva il flag -parameters per il tuo compilatore.

<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

Ho lottato anche con questo per un momento. Ma guardando la documentazione qui posso vedere che il parametro di annotazione onConstructor è considerato sperimentale e non è supportato bene sul mio IDE (STS 4). Secondo la documentazione di Jackson, i membri privati ​​non vengono (de) serializzati per impostazione predefinita. Ci sono modi rapidi per risolvere questo problema.

Aggiungi l'annotazione JsonAutoDetect e impostala in modo appropriato per rilevare i membri protetti / privati. Questo è conveniente per i DTO

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

Aggiungi una funzione di fabbrica con l'annotazione @JsonCreator, funziona meglio se hai bisogno di convalida di oggetti o trasformazioni aggiuntive.

public class SomeClass {

   // some code here

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

Ovviamente potresti anche aggiungere manualmente il costruttore anche in te stesso.


-1

Ho avuto un problema diverso ed era con i tipi primitivi booleani.

private boolean isAggregate;

Di conseguenza, generava il seguente errore

Exception: Unrecognized field "isAggregate" (class 

Lambok si converte isAggregatein isAggregate()getter rendendo la proprietà internamente a lombok come aggregateinvece isAggregate. Alla biblioteca di Jackson non piace e ha bisogno di isAggregateproprietà.

Ho aggiornato il booleano primitivo in Wrapper Boolean per aggirare questo problema. Ci sono altre opzioni per te se hai a che fare con i booleantipi, vedi il riferimento sotto.

Sol:

private Boolean isAggregate;

rif: https://www.baeldung.com/lombok-getter-boolean


Hai provato a utilizzare aggregate invece di isAggregate con il tipo primitivo? Credo che possa anche risolvere l'errore. Si prega di consultare anche questo stackoverflow.com/a/42620096/6332074
Muhammad Waqas Dilawar
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.