Изключения на Джаксън - проблеми и решения

1. Общ преглед

В този урок ще разгледаме най-често срещаните Джаксън Изключения - на JsonMappingException и UnrecognizedPropertyException .

И накрая - накратко ще обсъдим Джаксън без такива грешки в метода.

2. “ JsonMappingException : Не може да се конструира екземпляр на”

2.1. Проблемът

Първо - нека да разгледаме Jsonmappingexception: Не може да се конструира инстанция на.

Това изключение се изхвърля, ако Джаксън не може да създаде екземпляр на класа - това се случва, ако класът е абстрактен или е просто интерфейс .

В следния пример - ние се опитваме да десериализираме екземпляр от клас Zoo, който има свойство животно с абстрактен тип 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() { } }

Когато се опитваме да десериализираме JSON String към екземпляр на Zoo, той изхвърля „Jsonmappingexception: Can't Construct Instance Of“, както е в следния пример:

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

Най- пълен изключение е:

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. Решения

Можем да разрешим проблема с проста анотация - @JsonDeserialize за абстрактния клас:

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

Ако имаме повече от един подтип на абстрактния клас, тогава трябва да обмислим включването на информация за подтипа, както е показано в тази публикация: Наследяване с Джаксън.

3. JsonMappingException : Няма подходящ конструктор

3.1. Проблемът

Сега - нека разгледаме често срещаното Jsonmappingexception: Не е намерен подходящ конструктор за тип .

Това изключение се изхвърля, ако Джаксън не може да получи достъп до конструктора .

В следния пример - клас Потребител няма конструктор по подразбиране:

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

Когато се опитваме да десериализираме JSON низ за потребител, се извежда изключение „Jsonmappingexception: Не е намерен подходящ конструктор“ - както е в следния пример:

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

Най- пълен изключение е:

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. Решението

За да разрешите този проблем - просто добавете конструктор по подразбиране, както е в следния пример:

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

Сега, когато десериализираме - процесът ще работи добре:

@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 : Името на корен не отговаря на очакваното

4.1. Проблемът

След това - нека да разгледаме Jsonmappingexception: Името на корен не отговаря на очакваното.

Това изключение се изхвърля, ако JSON не съвпада точно с това, което търси Джаксън ; например основният JSON може да бъде обвит като в следния пример:

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

Най- пълен изключение е:

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. Решението

Можем да разрешим този проблем, като използваме анотацията @JsonRootName - както е в следния пример:

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

Когато се опитваме да десериализираме опакования JSON - той работи правилно:

@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 : Не е намерен сериализатор за клас

5.1. Проблемът

Сега - нека да разгледаме Jsonmappingexception: Не е намерен сериализатор за клас.

Това изключение се изхвърля, ако се опитате да сериализирате екземпляр, докато неговите свойства и техните гетери са частни .

В следния пример - ние се опитваме да сериализираме „ UserWithPrivateFields “:

public class UserWithPrivateFields { int id; String name; }

Когато се опитваме да сериализираме екземпляр на „ UserWithPrivateFields “ - се извежда изключение „Jsonmappingexception: Не е намерен сериализатор за клас“, както в следния пример:

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

Пълното изключение е:

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. Решението

Можем да разрешим този проблем, като конфигурираме видимостта на ObjectMapper - както е в следния пример:

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

Или с помощта на анотацията @JsonAutoDetect - както е в следния пример:

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

Разбира се, ако имаме опцията да модифицираме източника на класа, можем да добавим и гетери, които Джаксън да използва.

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

В тази статия се задълбочихме в най-често срещаните проблеми на Джаксън - изключения и грешки , разглеждайки потенциалните причини и решенията за всеки един от тях.

Внедряването на всички тези примери и кодови фрагменти може да бъде намерено на Github - това е проект, базиран на Maven, така че трябва да е лесно да се импортира и да се изпълнява както е.