Tidak bisa membuat Jackson dan Lombok bekerja sama

91

Saya sedang bereksperimen dalam menggabungkan Jackson dan Lombok. Itu adalah kelasku:

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

}

Itu adalah JAR yang saya tambahkan ke classpth:

Saya mengompilasinya dengan Netbeans (menurut saya ini tidak benar-benar relevan, tetapi saya tetap melaporkannya agar dapat direproduksi dengan sempurna dan setia). Lima JAR di atas disimpan dalam folder bernama " lib" di dalam folder proyek (bersama dengan " src", " nbproject", " test" dan " build"). Saya menambahkannya ke Netbeans melalui tombol " Tambahkan JAR / Folder " di properti proyek dan mereka terdaftar dalam urutan yang tepat seperti daftar di atas. Proyek ini adalah proyek jenis "aplikasi Java" standar.

Lebih lanjut, proyek Netbeans dikonfigurasi untuk " JANGAN dikompilasi saat disimpan ", " buat info debugging ", " laporkan API yang tidak digunakan lagi ", " lacak ketergantungan java ", " aktifkan pemrosesan anotasi ", dan " aktifkan pemrosesan anotasi di editor ". Tidak ada pemroses anotasi atau opsi pemrosesan anotasi yang dikonfigurasi secara eksplisit di Netbeans. Selain itu, -Xlint:allopsi baris perintah " " diteruskan di baris perintah kompilator, dan kompilator dijalankan pada VM eksternal.

Versi javac saya adalah 1.8.0_72 dan versi java saya adalah 1.8.0_72-b15. Netbeans saya adalah 8.1.

Proyek saya terkompilasi dengan baik. Namun, itu membuat pengecualian dalam eksekusinya. Pengecualian tampaknya tidak menjadi sesuatu yang terlihat mudah atau jelas dapat diperbaiki. Berikut adalah outputnya, termasuk 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

Saya sudah mencoba mencolek secara acak dengan anotasi @Valuedan @AllArgsConstructor, tetapi saya tidak bisa membuatnya lebih baik.

Saya mencari pengecualian di Google dan menemukan laporan bug lama tentang jackson , dan satu lagi yang terbuka, tetapi tampaknya terkait dengan sesuatu yang lain . Namun, ini masih tidak memberi tahu apa-apa tentang bug ini atau bagaimana cara memperbaikinya. Juga, saya tidak dapat menemukan sesuatu yang berguna mencari itu di tempat lain.

Karena apa yang saya coba lakukan adalah penggunaan yang sangat mendasar dari lombok dan jackson, tampaknya aneh bahwa saya tidak dapat menemukan informasi yang lebih berguna tentang cara mengatasi masalah ini. Mungkin saya melewatkan sesuatu?

Selain hanya mengatakan " jangan gunakan lombok " atau " jangan gunakan jackson ", apakah ada yang tahu tentang cara mengatasinya?

Victor Stafusa
sumber

Jawaban:

58

Jika Anda ingin POJO yang dapat diubah tetapi dapat diserialkan menggunakan lombok dan jackson. Gunakan penjelasan baru jacksons pada pembuat lomboks Anda @JsonPOJOBuilder(withPrefix = "") Saya mencoba solusi ini dan itu bekerja dengan sangat baik. Penggunaan sampel

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 {

    }
}

Jika Anda memiliki terlalu banyak kelas dengan @Builderdan Anda tidak ingin kode boilerplate mengosongkan anotasi, Anda dapat mengganti interseptor anotasi menjadi kosongwithPrefix

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

Dan Anda dapat menghapus kelas pembangun kosong dengan @JsonPOJOBuilderanotasi.

ganesan dharmalingam
sumber
Tautan ke sebuah solusi diperbolehkan, tetapi harap pastikan jawaban Anda berguna tanpanya: tambahkan konteks di sekitar tautan sehingga sesama pengguna Anda akan tahu apa itu dan mengapa itu ada, lalu kutip bagian paling relevan dari halaman Anda ' menautkan ulang jika halaman target tidak tersedia. Jawaban yang tidak lebih dari sebuah tautan dapat dihapus.
geisterfurz007
Solusi ini menjaga kekekalan saat bekerja di sekitar masalah deserialisation berbasis konstruktor yang mati otak (konstruktor multi-bidang memerlukan @JsonProperty pada setiap argumen konstruktor, daripada mencoba sesuatu yang cerdas). (Saya telah mencoba onConstructor _ = {@ JsonCreator} tetapi tidak berhasil)
JeeBee
35

Immutable + Lombok + Jackson dapat dicapai dengan cara berikut:

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);
    }
}
Alex Efimov
sumber
4
Bukankah AllArgsConstructorsudah menjadi bagian dari @Valueanotasi?
Zon
8
Menambahkan eksplisit @NoArgsConstructormenimpa konstruktor yang dihasilkan oleh @Valueanotasi, jadi Anda harus menambahkan ekstra@AllArgsConstructor
Davio
1
Solusi terbaik yang saya temukan - menurut saya - tanpa pola Builder. Terima kasih.
Radouxca
14

Saya mencoba beberapa cara di atas dan semuanya temperamental. Apa yang benar-benar berhasil bagi saya adalah jawaban yang saya temukan di sini .

pada direktori root proyek Anda, tambahkan file lombok.config (jika Anda belum melakukannya)

lombok.config

dan di dalam tempel ini

lombok.anyConstructor.addConstructorProperties=true

Kemudian Anda dapat menentukan pojos Anda seperti berikut:

@Data
@AllArgsConstructor
public class MyPojo {

    @JsonProperty("Description")
    private String description;
    @JsonProperty("ErrorCode")
    private String errorCode;
}
Giorgos Lamprakis
sumber
2
Tapi bukankah ini bisa berubah?
vphilipnyc
agar tidak dapat diubah, cukup ubah data dengan Getter and Builder atau RequiredArgsConstructor dan tandai semua bidang sebagai final
nekperu15739
8

Saya mengalami masalah yang persis sama, "menyelesaikannya" dengan menambahkan suppressConstructorProperties = trueparameter (menggunakan contoh Anda):

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

Jackson tampaknya tidak menyukai java.beans.ConstructorProperties penjelasan yang ditambahkan ke konstruktor. The suppressConstructorProperties = trueparameter memberitahu Lombok tidak menambahkannya (hal ini secara default).

Donovan Muller
sumber
7
suppressConstructorPropertiessekarang tidak digunakan lagi :-(
lilalinux
3
Yang tidak menjadi masalah, karena default baru juga false.
Michael Piefel
4

@AllArgsConstructor(suppressConstructorProperties = true)sudah ditinggalkan. Tentukan lombok.anyConstructor.suppressConstructorProperties=true( https://projectlombok.org/features/configuration ) dan ubah anotasi lombok POJO dari @Valuemenjadi @Data+ @NoArgsConstructor+ @AllArgsConstructorberfungsi untuk saya.

yyy
sumber
14
Ini mengubah kelas menjadi bisa berubah, yang saya asumsikan bukan yang diinginkan OP
Amit Goldstein
2

Bagi saya ini berhasil ketika saya telah memperbarui versi lombok ke: 'org.projectlombok: lombok: 1.18.0'

fascynacja.dll
sumber
2

Dari Jawaban Jan Rieke

Sejak lombok 1.18.4, Anda dapat mengonfigurasi penjelasan apa yang disalin ke parameter konstruktor. Masukkan ini ke dalam lombok.config:

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

Kemudian tambahkan saja @JsonPropertyke bidang Anda:

...

Anda akan membutuhkan @JsonProperty di setiap bidang meskipun namanya cocok, tetapi itu adalah praktik yang baik untuk diikuti. Anda juga dapat mengatur bidang Anda ke final publik menggunakan ini, yang saya lebih suka daripada getter.

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

Ini juga harus bekerja dengan getter (+ setter).

Mingwei Samuel
sumber
1
Ini menyelesaikan masalah saya! Terima kasih banyak! Saya terjebak pada integrasi musim semi-boot dan serialisasi antara JSON dan POJO dan mendapatkan kesalahan: Can not deserialize instance of java.lang.String out of START_OBJECT token... Dan ini memperbaikinya! Saya perlu memiliki @JsonPropertyanotasi itu untuk setiap parameter konstruktor dan tambahan itu ke @AllArgsConstructorallow lombok ke instrumennya.
Mark
1

Anda bisa membuat Jackson bermain dengan apa saja jika Anda menggunakan pola "mixin" . Pada dasarnya, ini memberi Anda cara untuk menambahkan anotasi Jackson ke kelas yang ada tanpa benar-benar memodifikasi kelas itu. Saya cenderung merekomendasikannya di sini daripada solusi Lombok karena ini memecahkan masalah yang dialami Jackson dengan fitur Jackson, jadi kemungkinan besar berfungsi dalam jangka panjang.

David Ehrmann
sumber
1

Saya semua kelas saya diberi keterangan seperti ini:

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

Ini bekerja dengan semua versi Lombok dan Jackson selama, setidaknya, beberapa tahun.

Contoh:

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

Dan itu dia. Lombok dan Jackson bermain bersama seperti pesona.

dimana_
sumber
9
Ini adalah kelas yang bisa berubah.
ŁukaszG
0
@JsonInclude(JsonInclude.Include.NON_NULL)
@Data
public class Person {
   String id;
   String first;
   String last;
}

Tambahan untuk Data Class, itu harus dikonfigurasi dengan benar ObjectMapper. Dalam hal ini, ini berfungsi dengan baik dengan konfigurasi ParameterNamesModule, dan pengaturan visibilitas Fields dan Metode Pembuat

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

Maka itu harus bekerja seperti yang diharapkan.

Hector Delgado
sumber
0

Saya mengalami masalah dengan membuat Lombok tidak menambahkan ConstructorProperiesanotasi, jadi pergi ke arah lain dan menonaktifkan Jackson dari melihat anotasi itu.

Pelakunya adalah JacksonAnnotationIntrospector.findCreatorAnnotation . Memperhatikan:

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

Perhatikan juga JacksonAnnotationIntrospector.setConstructorPropertiesImpliesCreator :

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

Jadi dua opsi, baik setel MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIESke salah atau buat satu JacksonAnnotationIntrospectorset setConstructorPropertiesImpliesCreatorke falsedan setel ini AnnotationIntrospectorke ObjectMappervia ObjectMapper.setAnnotationIntrospector .

Perhatikan beberapa hal, saya menggunakan Jackson 2.8.10 dan dalam versi MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIESitu tidak ada. Saya tidak yakin versi Jackson mana yang ditambahkan. Jadi kalau tidak ada, pakai JacksonAnnotationIntrospector.setConstructorPropertiesImpliesCreatormekanismenya.

John B
sumber
0

Anda juga perlu memiliki modul ini. https://github.com/FasterXML/jackson-modules-java8

kemudian aktifkan tanda -parameters untuk kompilator Anda.

<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>
Matt Broekhuis
sumber
0

Saya berjuang dengan ini sejenak juga. Tetapi melihat melalui dokumentasi di sini saya dapat melihat bahwa parameter anotasi onConstructor dianggap eksperimental dan tidak didukung dengan baik di IDE saya (STS 4). Menurut dokumentasi Jackson, anggota pribadi tidak (de) diserialkan secara default. Ada cara cepat untuk mengatasinya.

Tambahkan anotasi JsonAutoDetect dan setel dengan tepat untuk mendeteksi anggota yang dilindungi / pribadi. Ini nyaman untuk DTO

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

Tambahkan fungsi pabrik dengan anotasi @JsonCreator, ini bekerja paling baik jika Anda memerlukan beberapa validasi objek atau transformasi tambahan.

public class SomeClass {

   // some code here

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

Tentu saja Anda juga dapat menambahkan konstruktor pada diri Anda sendiri secara manual.

pengguna3416682
sumber
-1

Saya memiliki masalah yang berbeda dan itu dengan tipe primitif boolean.

private boolean isAggregate;

Akibatnya, muncul kesalahan berikut

Exception: Unrecognized field "isAggregate" (class 

Lambok bertobat isAggregateuntuk isAggregate()sebagai getter membuat properti internal lombok sebagai aggregategantinya isAggregate. Perpustakaan Jackson tidak menyukainya dan ia membutuhkan isAggregateproperti sebagai gantinya.

Saya memperbarui boolean primitif ke Wrapper Boolean untuk mengatasi masalah ini. Ada opsi lain untuk Anda jika Anda berurusan dengan booleantipe, lihat referensi di bawah.

Sol:

private Boolean isAggregate;

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

Zeus
sumber
Apakah Anda mencoba menggunakan agregat daripada isAggregate dengan tipe primitif? Saya yakin, ini juga bisa mengatasi kesalahan. Silakan juga lihat stackoverflow
ini.com/a/42620096/6332074