Pengecualian Jackson - Masalah dan Penyelesaian

1. Gambaran keseluruhan

Dalam tutorial ini, kita akan pergi ke atas paling Pengecualian biasa Jackson - yang JsonMappingException dan UnrecognizedPropertyException .

Akhirnya - kami akan membincangkan secara ringkas Jackson tidak ada kesalahan kaedah tersebut.

2. " JsonMappingException : Tidak Dapat Membangunkan Contoh"

2.1. Masalah

Pertama - mari kita lihat Jsonmappingexception: Tidak Dapat Membangunkan Instance Of.

Pengecualian ini dilemparkan jika Jackson tidak dapat membuat contoh kelas - ini berlaku jika kelas itu abstrak atau hanya antara muka .

Dalam contoh berikut - kami cuba untuk deserialize contoh dari kelas Zoo yang mempunyai harta haiwan dengan abstrak jenis haiwan :

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() { } }

Ketika kita berusaha untuk mendeserialisasikan contoh JSON String to Zoo, ia akan melontarkan "Jsonmappingexception: Can Not Construct Instance Of" seperti 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 ialah:

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. Penyelesaian

Kami dapat menyelesaikan masalah dengan penjelasan sederhana - @JsonDeserialize pada kelas abstrak:

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

Sekiranya kita mempunyai lebih daripada satu subtipe kelas abstrak, maka kita harus mempertimbangkan untuk memasukkan maklumat subjenis seperti yang ditunjukkan dalam catatan ini: Warisan dengan Jackson.

3. JsonMappingException : Tiada Pembina yang Sesuai

3.1. Masalah

Sekarang - mari kita lihat Jsonmappingexception yang biasa: Tidak Ada Pembina yang Sesuai untuk jenis .

Pengecualian ini dilemparkan jika Jackson tidak dapat mengakses pembangunnya .

Dalam contoh berikut - Pengguna kelas tidak mempunyai konstruktor lalai:

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

Apabila kita cuba mendeserialisasikan String JSON kepada Pengguna, terdapat pengecualian "Jsonmappingexception: Tidak Sesuai Pembina yang dijumpai" dilemparkan - seperti dalam 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 ialah:

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. Penyelesaian

Untuk menyelesaikan masalah ini - tambahkan konstruktor lalai seperti 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 apabila kita melakukan deserialisasi - prosesnya akan berjalan lancar:

@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 Akar Tidak Sesuai Dijangka

4.1. Masalah

Next – let's take a look at Jsonmappingexception: Root Name Does Not Match Expected.

This exception is thrown if the JSON doesn't match exactly what Jackson is looking for; for example, the main JSON could be wrapped as in the following example:

@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 full exception is:

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. The Solution

We can solve this problem using the annotation @JsonRootName – as in the following example:

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

When we try to deserialize the wrapped JSON – it works correctly:

@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: No Serializer Found for Class

5.1. The Problem

Now – let's take a look at Jsonmappingexception: No Serializer Found for Class.

This exception is thrown if you try to serialize an instance while its properties and their getters are private.

In the following example – we try to serialize a “UserWithPrivateFields“:

public class UserWithPrivateFields { int id; String name; }

When we try to serialize an instance of “UserWithPrivateFields” – an Exception “Jsonmappingexception: No Serializer Found for Class” is thrown as in the following example:

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

The full exception is:

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. The Solution

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

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

Or using the annotation @JsonAutoDetect – as in the following example:

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

Of course, if we do have the option to modify the source of the class, we can also add in getters for Jackson to use.

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 biasa - pengecualian dan kesilapan , melihat kemungkinan penyebabnya dan mencari jalan keluar untuk setiap masalah tersebut.

Pelaksanaan semua contoh dan coretan kod ini boleh didapati di Github - ini adalah projek berasaskan Maven, jadi mudah diimport dan dijalankan sebagaimana adanya.