Jackson 異常處理 – 問題與解決方案

Data,Jackson
Remote
1
12:06 AM · Dec 01 ,2025

1. 概述在本教程中,我們將介紹最常見的 Jackson 異常——JsonMappingException,UnrecognizedPropertyException,MismatchedInputException

最後,我們將簡要討論 Jackson 的“No such method” 錯誤。

2. JsonMappingException: Cannot Construct Instance Of

2.1. The Problem

首先,讓我們看一下 JsonMappingException: Cannot Construct Instance Of。

Jackson 無法創建類的實例時,此異常會被拋出,這發生在類是抽象的,或者僅僅是一個接口

我們將嘗試從 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: Cannot 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. Solutions

我們可以使用簡單的註解 — @JsonDeserialize 在抽象類上解決這個問題:

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

注意,如果抽象類有多個子類型,我們應該考慮包括子類型信息,如“Inheritance With Jackson”文章所示。

3. JsonMappingException: No Suitable Constructor

3.1. The Problem

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

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

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

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:

@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

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

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

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

@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

現在讓我們來查看 JsonMappingException: No Serializer Found for Class。

這個異常是在我們嘗試序列化實例時,其屬性和它們的 getter 方法是私有的時拋出的。

我們將嘗試序列化一個 UserWithPrivateFields

public class UserWithPrivateFields {
    int id;
    String name;
}

當我們嘗試序列化 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);
}

這是 完整異常

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

我們可以通過配置 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 { ... }

當然,如果我們有修改類的源文件的選項,我們也可以為 Jackson 使用添加 getter 方法。

6. JsonMappingException: Cannot Deserialize Instance Of

6.1. The Problem

接下來,讓我們看一下 JsonMappingException: Cannot Deserialize Instance Of

當反序列化時,如果使用了錯誤的類型,則會拋出此異常。

在下面的示例中,我們嘗試反序列化一個 List 中的 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);
}

下面是完整的異常信息:

com.fasterxml.jackson.databind.JsonMappingException:
Cannot 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

我們可以通過將類型從 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 users = mapper.reader()
      .forType(new TypeReference>() {})
      .readValue(json);

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

public class Person {

    private String firstName;
    private String lastName;
    private String contact;

    // standard getters and setters
}

{
    "firstName":"Azhrioun",
    "lastName":"Abderrahim",
    "contact":{
        "email":"[email protected]"
    }
}

Cannot deserialize value of type `java.lang.String` from Object value (token `JsonToken.START_OBJECT`)
 at [Source: (String)"{"firstName":"Azhrioun","lastName":"Abderrahim","contact":{"email":"[email protected]"}}"; line: 1, column: 59] (through reference chain: com.baeldung.exceptions.Person["contact"])
...

@Test
public void givenJsonObject_whenDeserializingIntoString_thenException() throws IOException {
    final String json = "{\"firstName\":\"Azhrioun\",\"lastName\":\"Abderrahim\",\"contact\":{\"email\":\"[email protected]\"}}";
    final ObjectMapper mapper = new ObjectMapper();

    Exception exception = assertThrows(JsonMappingException.class, () -> mapper.reader()
      .forType(Person.class)
      .readValue(json));

    assertTrue(exception.getMessage()
      .contains("Cannot deserialize value of type `java.lang.String` from Object value (token `JsonToken.START_OBJECT`)"));
}

public class Contact { private String email; // standard getters and setters }

public class PersonContact {

    private String firstName;
    private String lastName;
    private Contact contact;

    // standard getters and setters
}

@Test
public void givenJsonObject_whenDeserializingIntoObject_thenDeserialize() throws IOException {
    final String json = "{\"firstName\":\"Azhrioun\",\"lastName\":\"Abderrahim\",\"contact\":{\"email\":\"[email protected]\"}}";
    final ObjectMapper mapper = new ObjectMapper();

    PersonContact person = mapper.reader()
      .forType(PersonContact.class)
      .readValue(json);

    assertEquals("[email protected]", person.getContact().getEmail());
}

現在讓我們看看

當在反序列化過程中存在時,此異常將被拋出。

我們將嘗試反序列化包含額外屬性“”的 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);
}

這是完整的異常:

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)

我們可以通過配置來解決此問題:

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

或者我們可以使用註解

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

9. JsonParseException: 意外字符 (”’ (代碼 39))

9.1. 問題

接下來,我們討論 JsonParseException: 意外字符 (”’ (代碼 39))

此異常在 JSON 字符串包含單引號而不是雙引號 的情況下被拋出。

我們將嘗試反序列化包含單引號的 JSON 字符串:

@Test(expected = JsonProcessingException.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);
}

以下是 完整的異常

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)

9.2. 解決方案

我們可以通過配置 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);
}

10. : Unexpected Character (‘c’ (code n))

在本節中,我們將解決另一個常見的解析異常,。此異常發生在 JSON 解析器在解析過程中遇到意外字符時。異常消息中指示的特定意外字符(‘c’)和其 Unicode 碼點(‘n’

10.1. 問題

異常通常由於 JSON 字符串中的語法錯誤或無效字符而導致。這些錯誤可能源於諸如缺失逗號、JSON 對象或數組的錯誤嵌套或存在不在 JSON 語法中允許的字符等問題。

讓我們嘗試反序列化包含單引號周圍缺少雙引號的 ‘name’ 字段的 JSON 字符串:

@Test(expected = JsonProcessingException.class)
public void givenInvalidJsonString_whenDeserializing_thenException() throws JsonProcessingException, IOException {
    String json = "{\"id\":1, name:\"John\"}"; // Missing double quotes around 'name'
    ObjectMapper mapper = new ObjectMapper();
    mapper.reader().forType(User.class).readValue(json);
}

此時的異常信息如下:

com.fasterxml.jackson.core.JsonParseException: Unexpected character ('n' (code 110)): 
was expecting double-quote to start field name
at [Source: (String)"{"id":1, name:"John"}"; line: 1, column: 11]
at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java.2477)

10.2. 解決方案

為了解決 異常,必須仔細檢查 JSON 字符串是否存在語法錯誤,並確保其符合 JSON 規範。

常見的解決方案包括糾正語法錯誤,如缺失或位置錯誤的逗號、確保對象和數組的正確嵌套以及刪除不在 JSON 語法中允許的字符。

11. MismatchedInputException: Cannot Deserialize Instance

The MismatchedInputException exception has different variants. It can occur when we do not provide a default constructor for a POJO class or do not annotate constructor parameters for immutable fields with @JsonProperty.

Also, the exception can occur if we deserialize an incompatible JSON array into a Java object.

11.1. The Problem: No Default Constructor

This exception occurs when trying to deserialize a JSON string into a POJO class without a default or no-arg constructor. Since Jackson needs to instantiate the target class, not having a default constructor will result in an exception.

To demonstrate the exception, let’s create a class named Book:

class Book {
    private int id;
    private String title;
    
    public Book(int id, String title) {
        this.id = id;
        this.title = title; 
    }  
    // getters and setters
}

Here, we create a POJO class without the default constructor.

Next, let’s map a JSON string to the POJO class:

@Test
void givenJsonString_whenDeserializingToBook_thenIdIsCorrect() throws JsonProcessingException {
    String jsonString = "{\"id\":\"10\",\"title\":\"Harry Potter\"}";
    ObjectMapper mapper = new ObjectMapper();
    Book book = mapper.readValue(jsonString, Book.class);
    assertEquals(book.getId(),10);
}

The above test throws a MismatchedInputException:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: 
Cannot construct instance of `com.baeldung.mismatchedinputexception.Book` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{"id":"10","title":"Harry Potter"}"; line: 1, column: 2]

11.2. The Solution

Let’s solve this problem by adding the default constructor to the Book class:

// ...   
public Book() {
}
// ...

The test that produces the error now passes with the introduction of the default constructor.

11.3. The Problem: Missing @JsonProperty Annotation

A POJO class that has a field marked as final cannot have a default constructor if the field isn’t initialized at the time of its declaration. This is because the final field must be set via the constructor or directly at the declaration.

Since there’s no default constructor, deserializing JSON into a class instance fails with MismatchedInputException.

Let’s create a POJO class named Animals with an immutable field:

class Animals {
    private final int id;
    private String name;
    
    public Animals(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

Then, let’s map a JSON string to the POJO class:

@Test
void givenJsonString_whenDeserializingToJavaObjectWithImmutableField_thenIdIsCorrect() throws JsonProcessingException {
    String jsonString = "{\"id\":10,\"name\":\"Dog\"}";
    ObjectMapper mapper = new ObjectMapper();
    Animals animal = mapper.readValue(jsonString, Animals.class);
    assertEquals(animal.getId(),10);
}

Here, we assert that the id is equal to an expected id. However, this throws an exception:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: 
Cannot construct instance of `com.baeldung.mismatchedinputexception.Animals` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{"id":10,"name":"Dog"}"; line: 1, column: 2]

11.4. Solution

We can solve this problem by annotating the parameters with @JsonProperty:

Animals(@JsonProperty("id") int id, @JsonProperty("name") String name) {
   this.id = id;
   this.name = name;
}

11.5. The Problem: Incompatible Java Object

Additionally, this error also occurs when Jackson tries to deserialize a JSON array into a Java object that’s incompatible.

To stage this error, let’s use the Book class we created earlier and pass the JSON array into a single Book object:

@Test
void givenJsonString_whenDeserializingToBookList_thenTitleIsCorrect() throws JsonProcessingException {
    String jsonString = "[{\"id\":\"10\",\"title\":\"Harry Potter\"}]";
    ObjectMapper mapper = new ObjectMapper();
    Book book = mapper.readValue(jsonString, Book.class);
    assertEquals(book.getTitle(),"Harry Potter");
}

In the code above, we map a JSON array to a single object. This throws an exception:

com.fasterxml.jackson.databind.exc.MismatchedInputException: 
Cannot deserialize value of type `com.baeldung.mismatchedinputexception.Book` from Array value (token `JsonToken.START_ARRAY`)
at [Source: (String)"[{"id":"10","title":"Harry Potter"}]"; line: 1, column: 1]

11.6. The Solution

Let’s solve this problem by deserializing the JSON array in a List object:

@Test
void givenJsonString_whenDeserializingToBookList_thenTitleIsCorrect() throws JsonProcessingException {
    String jsonString = "[{\"id\":\"10\",\"title\":\"Harry Potter\"}]";
    ObjectMapper mapper = new ObjectMapper();
    List<Book> book = mapper.readValue(jsonString, new TypeReference<List<Book>>(){});
    assertEquals(book.get(0).getTitle(),"Harry Potter");
}

However, our intention may be to deserialize a single JSON string instead of a JSON array. We can remove the square bracket from the jsonString and map it to a single Java object.

12. Jackson NoSuchMethodError

最後,我們快速討論一下 Jackson 的 “No such method” 錯誤。

java.lang.NoSuchMethodError 異常被拋出時,通常是因為我們的 classpath 上存在多個(且不兼容)版本的 Jackson jar。

這是完整的 異常:

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

13. 結論在本文中,我們深入研究了最常見的 Jackson 問題——異常和錯誤,分析了每個問題的潛在原因以及解決方案。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.