Jackson Exceptions – Problems and Solutions – Jackson的例外情况 – 问题和解决方案

最后修改: 2014年 12月 20日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll go over the most common Jackson ExceptionsJsonMappingException and UnrecognizedPropertyException.

在本教程中,我们将讨论最常见的Jackson异常JsonMappingExceptionUnrecognizedPropertyException

Finally, we’ll briefly discuss Jackson “No such method” errors.

最后,我们将简要地讨论杰克逊 “没有这样的方法 “的错误。

2. JsonMappingException: Can Not Construct Instance Of

2、JsonMappingException 无法构建实例

2.1. The Problem

2.1.问题

First, let’s take a look at JsonMappingException: Can Not Construct Instance Of.

首先,我们来看看JsonMappingException。无法构建实例。

This exception is thrown if Jackson can’t create an instance of the class, which happens if the class is abstract or it is just an interface.

如果Jackson不能创建该类的实例,就会抛出这个异常,如果该类是抽象的或者它只是一个接口,就会出现这种情况。

Here we’ll try to deserialize an instance from class Zoo that has a property animal with abstract type Animal:

在这里,我们将尝试反序列化一个来自Zoo类的实例,它有一个属性animal,其类型为abstractAnimal。

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

When we try to deserialize a JSON String to Zoo instance, it throws the JsonMappingException: Can Not Construct Instance Of:

当我们试图将一个JSON String反序列化为Zoo实例时,会抛出JsonMappingException。Can Not 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);
}

This is the full exception:

这是完整的例外

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

2.2.解决方案

We can solve the problem with a simple annotation — @JsonDeserialize on the abstract class:

我们可以用一个简单的注解来解决这个问题–@JsonDeserialize在抽象类上。

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

Note that if we have more than one subtype of the abstract class, we should consider including subtype information as shown in the article Inheritance With Jackson.

请注意,如果我们有一个以上的抽象类的子类型,我们应该考虑包括子类型信息,如文章Inheritance With Jackson中所示。

3. JsonMappingException: No Suitable Constructor

3.JsonMappingException: No Suitable Constructor

3.1. The Problem

3.1.问题

Now let’s look at the common JsonMappingException: No Suitable Constructor found for type.

现在我们来看看常见的JsonMappingException。没有为类型找到合适的构造函数

This exception is thrown if Jackson can’t access the constructor.

如果Jackson无法访问构造函数,就会抛出这个异常。

In the following example, class User doesn’t have a default constructor:

在下面的例子中,类User没有一个默认的构造函数。

public class User {
    public int id;
    public String name;

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

When we try to deserialize a JSON String to User, JsonMappingException: No Suitable Constructor Found is thrown:

当我们试图将一个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);
}

And this is the full exception:

而这是完全的例外

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

3.2.解决方案

To solve this problem, we just add a default constructor:

为了解决这个问题,我们只需添加一个默认的构造函数。

public class User {
    public int id;
    public String name;

    public User() {
        super();
    }

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

Now when we deserialize, the process will work just fine:

现在当我们反序列化的时候,这个过程就可以正常工作了。

@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: Root Name Does Not Match Expected

4. JsonMappingException:根名称与预期的不一致

4.1. The Problem

4.1.问题

Next, let’s take a look at JsonMappingException: Root Name Does Not Match Expected.

接下来,我们来看看JsonMappingException。根名称与预期的不一致。

This exception is thrown if the JSON doesn’t match exactly what Jackson is looking for.

如果JSON与Jackson正在寻找的内容不完全匹配,就会抛出这个异常。

For example, the main JSON could be wrapped:

例如,主要的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);
}

This is the full exception:

这是完整的例外

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

4.2.解决方案

We can solve this problem using the annotation @JsonRootName:

我们可以使用注解@JsonRootName来解决这个问题。

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

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

当我们尝试反序列化被包装的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: No Serializer Found for Class

5.JsonMappingException: No Serializer Found for Class

5.1. The Problem

5.1.问题

Now let’s take a look at JsonMappingException: No Serializer Found for Class.

现在我们来看看JsonMappingException。没有找到类的串行器。

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

如果我们试图序列化一个实例,而其属性及其获取器是私有的,就会抛出这个异常。

We’ll try to serialize a UserWithPrivateFields:

我们将尝试序列化一个UserWithPrivateFields

public class UserWithPrivateFields {
    int id;
    String name;
}

When we try to serialize an instance of UserWithPrivateFields, JsonMappingException: No Serializer Found for Class is thrown:

当我们试图序列化一个UserWithPrivateFields的实例时,JsonMappingException。No Serializer Found for Class被抛出。

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

And this is the full exception:

而这是完全的例外

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

5.2.解决方案

We can solve this problem by configuring the ObjectMapper visibility:

我们可以通过配置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"));
}

Or we can use the annotation @JsonAutoDetect:

或者我们可以使用注解@JsonAutoDetect

@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.JsonMappingException 不能反序列化的实例

6.1. The Problem

6.1.问题

Next, let’s take a look at JsonMappingException: Can Not Deserialize Instance Of.

接下来,我们来看看JsonMappingException。Can Not Deserialize Instance Of.

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

如果在反序列化时使用了错误的类型,就会抛出这个异常。

In this example, we are trying to deserialize a List of User:

在这个例子中,我们试图反序列化一个ListUser

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

And here’s the full exception:

而这里有一个完整的例外

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

6.2.解决方案

We can solve this problem by changing the type from User to List<User>:

我们可以通过将类型从User改为List<User>来解决这个问题。

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

    assertEquals(2, users.size());
}

7. UnrecognizedPropertyException

7.UnrecognizedPropertyException

7.1. The Problem

7.1.问题

Now let’s see the UnrecognizedPropertyException.

现在我们来看看UnrecognizedPropertyException

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

如果在反序列化的过程中,JSON字符串中有一个未知的属性,就会抛出这个异常。

We’ll try to deserialize a JSON String with extra property “checked“:

我们将尝试反序列化一个带有额外属性”checked“的JSON字符串。

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

This is the full exception:

这是完整的例外

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

7.2.解决方案

We can solve this problem by configuring the ObjectMapper:

我们可以通过配置ObjectMapper来解决这个问题。

@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

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

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

8、JsonParseException 意外的字符(”‘ (code 39))

8.1. The Problem

8.1.问题

Next, let’s discuss JsonParseException: Unexpected character (”’ (code 39)).

接下来,让我们讨论一下JsonParseException。意外的字符(”‘(代码39))

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

如果要被反序列化的JSON字符串包含单引号而不是双引号,就会抛出这个异常。

We’ll try to deserialize a JSON String containing single quotes:

我们将尝试对一个包含单引号的JSON字符串进行反序列化。

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

Here’s the full exception:

这里有一个完整的例外

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

8.2.解决方案

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

我们可以通过配置ObjectMapper以允许单引号来解决这个问题。

@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

9.JacksonNoSuchMethodError

Finally, let’s quickly discuss the Jackson “No such method” errors.

最后,让我们快速讨论一下杰克逊的 “没有这样的方法 “错误。

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

java.lang.NoSuchMethodError异常被抛出时,通常是因为我们的classpath上有多个(且不兼容的)Jackson jars版本。

This is the full exception:

这是完全例外

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

10.结论

In this article, we did a deep dive into the most common Jackson problems — exceptions and errors — looking at the potential causes and at the solutions for each one.

在这篇文章中,我们对最常见的杰克逊问题–异常和错误进行了深入研究,看看每一个问题的潜在原因和解决方案。

The implementation of all these examples and code snippets can be found on GitHub. This is a Maven-based project, so it should be easy to import and run as it is.

所有这些例子和代码片段的实现都可以在GitHub上找到。这是一个基于Maven的项目,所以应该很容易导入并按原样运行。