Pengecualian Jackson - Masalah dan Solusinya

1. Ikhtisar

Dalam tutorial ini, kita akan membahas yang paling umum Jackson Pengecualian tersebut - yang JsonMappingException dan UnrecognizedPropertyException .

Akhirnya - kami akan secara singkat membahas Jackson tidak ada kesalahan metode seperti itu.

2. " JsonMappingException : Tidak Dapat Membuat Instans Dari"

2.1. Masalah

Pertama - mari kita lihat Jsonmappingexception: Can Not Construct Instance Of.

Pengecualian ini dilemparkan jika Jackson tidak dapat membuat instance kelas - ini terjadi jika kelas itu abstrak atau hanya antarmuka .

Pada contoh berikut - kami mencoba untuk menghilangkan sebuah instance dari kelas Zoo yang memiliki property animal dengan tipe abstrak Animal :

public class Zoo { public Animal animal; public Zoo() { } } abstract class Animal { public String name; public Animal() { } } class Cat extends Animal { public int lives; public Cat() { } }

Saat kami mencoba untuk mendesentralisasi instance JSON String ke Zoo, hal itu akan memunculkan "Jsonmappingexception: Can Not Construct Instance Of" seperti pada contoh berikut:

@Test(expected = JsonMappingException.class) public void givenAbstractClass_whenDeserializing_thenException() throws IOException { String json = "{"animal":{"name":"lacy"}}"; ObjectMapper mapper = new ObjectMapper(); mapper.reader().forType(Zoo.class).readValue(json); }

The pengecualian penuh adalah:

com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of org.baeldung.jackson.exception.Animal, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information at [Source: {"animal":{"name":"lacy"}}; line: 1, column: 2] (through reference chain: org.baeldung.jackson.exception.Zoo["animal"]) at c.f.j.d.JsonMappingException.from(JsonMappingException.java:148)

2.2. Solusi

Kita dapat menyelesaikan masalah dengan anotasi sederhana - @JsonDeserialize pada kelas abstrak:

@JsonDeserialize(as = Cat.class) abstract class Animal {...}

Jika kita memiliki lebih dari satu subtipe kelas abstrak, maka kita harus mempertimbangkan untuk memasukkan informasi subtipe seperti yang ditunjukkan dalam posting ini: Warisan dengan Jackson.

3. JsonMappingException : Tidak Ada Pembuat yang Sesuai

3.1. Masalah

Sekarang - mari kita lihat pengecualian umum Jsonmapping: Tidak ada Konstruktor yang Cocok ditemukan untuk tipe .

Pengecualian ini diberikan jika Jackson tidak dapat mengakses konstruktor .

Dalam contoh berikut - pengguna kelas tidak memiliki konstruktor default:

public class User { public int id; public String name; public User(int id, String name) { this.id = id; this.name = name; } }

Saat kami mencoba untuk mendesentralisasi String JSON ke Pengguna, sebuah Pengecualian "Jsonmappingexception: Tidak Ditemukan Konstruktor yang Cocok" - seperti pada contoh berikut:

@Test(expected = JsonMappingException.class) public void givenNoDefaultConstructor_whenDeserializing_thenException() throws IOException { String json = "{"id":1,"name":"John"}"; ObjectMapper mapper = new ObjectMapper(); mapper.reader().forType(User.class).readValue(json); }

The pengecualian penuh adalah:

com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class org.baeldung.jackson.exception.User]: can not instantiate from JSON object (need to add/enable type information?) at [Source: {"id":1,"name":"John"}; line: 1, column: 2] at c.f.j.d.JsonMappingException.from(JsonMappingException.java:148)

3.2. Solusinya

Untuk mengatasi masalah ini - cukup tambahkan konstruktor default seperti pada contoh berikut:

public class User { public int id; public String name; public User() { super(); } public User(int id, String name) { this.id = id; this.name = name; } }

Sekarang ketika kita melakukan deserialisasi - prosesnya akan bekerja dengan baik:

@Test public void givenDefaultConstructor_whenDeserializing_thenCorrect() throws IOException { String json = "{"id":1,"name":"John"}"; ObjectMapper mapper = new ObjectMapper(); User user = mapper.reader() .forType(User.class).readValue(json); assertEquals("John", user.name); }

4. JsonMappingException : Nama Root Tidak Sesuai Dengan Yang Diharapkan

4.1. Masalah

Selanjutnya - mari kita lihat Jsonmappingexception: Nama Root Tidak Sesuai dengan yang Diharapkan.

Pengecualian ini diberikan jika JSON tidak sama persis dengan yang dicari Jackson ; misalnya, JSON utama dapat digabungkan seperti pada contoh berikut:

@Test(expected = JsonMappingException.class) public void givenWrappedJsonString_whenDeserializing_thenException() throws IOException { String json = "{"user":{"id":1,"name":"John"}}"; ObjectMapper mapper = new ObjectMapper(); mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE); mapper.reader().forType(User.class).readValue(json); }

The pengecualian penuh adalah:

com.fasterxml.jackson.databind.JsonMappingException: Root name 'user' does not match expected ('User') for type [simple type, class org.baeldung.jackson.dtos.User] at [Source: {"user":{"id":1,"name":"John"}}; line: 1, column: 2] at c.f.j.d.JsonMappingException.from(JsonMappingException.java:148) 

4.2. Solusinya

Kami dapat menyelesaikan masalah ini menggunakan anotasi @JsonRootName - seperti pada contoh berikut:

@JsonRootName(value = "user") public class UserWithRoot { public int id; public String name; }

Saat kami mencoba untuk menghentikan derialisasi JSON yang dibungkus - ini berfungsi dengan benar:

@Test public void givenWrappedJsonStringAndConfigureClass_whenDeserializing_thenCorrect() throws IOException { String json = "{"user":{"id":1,"name":"John"}}"; ObjectMapper mapper = new ObjectMapper(); mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE); UserWithRoot user = mapper.reader() .forType(UserWithRoot.class) .readValue(json); assertEquals("John", user.name); }

5. JsonMappingException : Serializer Tidak Ditemukan untuk Kelas

5.1. Masalah

Sekarang - mari kita lihat Jsonmappingexception: No Serializer Found for Class.

Pengecualian ini muncul jika Anda mencoba untuk membuat serial sebuah instance sementara propertinya dan getternya bersifat pribadi .

Dalam contoh berikut - kami mencoba membuat serial " UserWithPrivateFields ":

public class UserWithPrivateFields { int id; String name; }

Saat kami mencoba membuat serial instance " UserWithPrivateFields " - Pengecualian "Jsonmappingexception: Tidak Ada Serializer yang Ditemukan untuk Kelas" ditampilkan seperti pada contoh berikut:

@Test(expected = JsonMappingException.class) public void givenClassWithPrivateFields_whenSerializing_thenException() throws IOException { UserWithPrivateFields user = new UserWithPrivateFields(1, "John"); ObjectMapper mapper = new ObjectMapper(); mapper.writer().writeValueAsString(user); }

Pengecualian lengkapnya adalah:

com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class org.baeldung.jackson.exception.UserWithPrivateFields and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) at c.f.j.d.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:59)

5.2. Solusinya

Kita dapat mengatasi masalah ini dengan mengkonfigurasi visibilitas ObjectMapper - seperti pada contoh berikut:

@Test public void givenClassWithPrivateFields_whenConfigureSerializing_thenCorrect() throws IOException { UserWithPrivateFields user = new UserWithPrivateFields(1, "John"); ObjectMapper mapper = new ObjectMapper(); mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY); String result = mapper.writer().writeValueAsString(user); assertThat(result, containsString("John")); }

Atau menggunakan anotasi @JsonAutoDetect - seperti pada contoh berikut:

@JsonAutoDetect(fieldVisibility = Visibility.ANY) public class UserWithPrivateFields { ... }

Tentu saja, jika kita memiliki opsi untuk memodifikasi sumber kelas, kita juga dapat menambahkan getter untuk digunakan Jackson.

6. JsonMappingException: Can Not Deserialize Instance Of

6.1. The Problem

Next – let's take a look at Jsonmappingexception: Can Not Deserialize Instance Of.

This exception is thrown if the wrong type is used while deserializing.

In the following example – we are trying to deserialize a List of User:

@Test(expected = JsonMappingException.class) public void givenJsonOfArray_whenDeserializing_thenException() throws JsonProcessingException, IOException { String json = "[{"id":1,"name":"John"},{"id":2,"name":"Adam"}]"; ObjectMapper mapper = new ObjectMapper(); mapper.reader().forType(User.class).readValue(json); }

The full exception is:

com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of org.baeldung.jackson.dtos.User out of START_ARRAY token at [Source: [{"id":1,"name":"John"},{"id":2,"name":"Adam"}]; line: 1, column: 1] at c.f.j.d.JsonMappingException.from(JsonMappingException.java:148)

6.2. The Solution

We can solve this problem by changing the type from User to List – as in the following example:

@Test public void givenJsonOfArray_whenDeserializing_thenCorrect() throws JsonProcessingException, IOException { String json = "[{"id":1,"name":"John"},{"id":2,"name":"Adam"}]"; ObjectMapper mapper = new ObjectMapper(); List users = mapper.reader() .forType(new TypeReference
    
     () {}) .readValue(json); assertEquals(2, users.size()); }
    

7. UnrecognizedPropertyException

7.1. The Problem

Now – let's see the UnrecognizedPropertyException.

This exception is thrown if there is an unknown property in the JSON String while deserializing.

In the following example – we try to deserialize a JSON String with extra property “checked“:

@Test(expected = UnrecognizedPropertyException.class) public void givenJsonStringWithExtra_whenDeserializing_thenException() throws IOException { String json = "{"id":1,"name":"John", "checked":true}"; ObjectMapper mapper = new ObjectMapper(); mapper.reader().forType(User.class).readValue(json); }

The full exception is:

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "checked" (class org.baeldung.jackson.dtos.User), not marked as ignorable (2 known properties: "id", "name"]) at [Source: {"id":1,"name":"John", "checked":true}; line: 1, column: 38] (through reference chain: org.baeldung.jackson.dtos.User["checked"]) at c.f.j.d.exc.UnrecognizedPropertyException.from( UnrecognizedPropertyException.java:51)

7.2. The Solution

We can solve this problem by configuring the ObjectMapper – as in the following example:

@Test public void givenJsonStringWithExtra_whenConfigureDeserializing_thenCorrect() throws IOException { String json = "{"id":1,"name":"John", "checked":true}"; ObjectMapper mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); User user = mapper.reader().forType(User.class).readValue(json); assertEquals("John", user.name); }

Or we can use the annotation @JsonIgnoreProperties:

@JsonIgnoreProperties(ignoreUnknown = true) public class User {...}

8. JsonParseException: Unexpected Character (”' (code 39))

8.1. The Problem

Next – let's discuss JsonParseException: Unexpected character (”' (code 39)).

This exception is thrown if the JSON String to be deserialized contains single quotes instead of double quotes.

In the following example – we try to deserialize a JSON String containing single quotes:

@Test(expected = JsonParseException.class) public void givenStringWithSingleQuotes_whenDeserializing_thenException() throws JsonProcessingException, IOException { String json = "{'id':1,'name':'John'}"; ObjectMapper mapper = new ObjectMapper(); mapper.reader() .forType(User.class).readValue(json); }

The full exception is:

com.fasterxml.jackson.core.JsonParseException: Unexpected character (''' (code 39)): was expecting double-quote to start field name at [Source: {'id':1,'name':'John'}; line: 1, column: 3] at c.f.j.core.JsonParser._constructError(JsonParser.java:1419)

8.2. The Solution

We can solve this by configuring the ObjectMapper to allow single quotes:

@Test public void givenStringWithSingleQuotes_whenConfigureDeserializing_thenCorrect() throws JsonProcessingException, IOException { String json = "{'id':1,'name':'John'}"; JsonFactory factory = new JsonFactory(); factory.enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES); ObjectMapper mapper = new ObjectMapper(factory); User user = mapper.reader().forType(User.class) .readValue(json); assertEquals("John", user.name); }

9. Jackson NoSuchMethodError

Finally – let's quickly discuss the Jackson “No such method” errors.

When java.lang.NoSuchMethodError Exception is thrown, it is usually because you have multiple (and incompatible) versions of Jackson jars on your classpath.

The full exception is:

java.lang.NoSuchMethodError: com.fasterxml.jackson.core.JsonParser.getValueAsString()Ljava/lang/String; at c.f.j.d.deser.std.StringDeserializer.deserialize(StringDeserializer.java:24)

10. Conclusion

Dalam artikel ini, kami menyelami masalah Jackson yang paling umum - pengecualian dan kesalahan , melihat penyebab potensial dan solusi untuk masing-masing masalah.

Penerapan semua contoh dan cuplikan kode ini dapat ditemukan di Github - ini adalah proyek berbasis Maven, jadi semestinya mudah untuk mengimpor dan menjalankan apa adanya.