1. 程式人生 > >Jackson異常問題和解決方案總結

Jackson異常問題和解決方案總結

轉載自https://blog.csdn.net/u010429286/article/details/78395775

1. 前言

在本教程中, 我們會回顧最常見的Jackson異常 – theJsonMappingException andUnrecognizedPropertyException.

最後,我們將會簡要討論Jackson 中的no such method錯誤。

2. “JsonMappingException: Can not construct instance of”

2.1. The Problem

首先我們來看JsonMappingException: Can not construct instance of

.

此異常是由Jackson不能完成一個物件的構造導致的 – 如果序反序列化的類是抽象類或者介面,就會導致這個異常。這裡給出一個例子 – 我們想要反序列化一個Zoo物件,Zoo中有一個屬性是抽象類Animal的物件:

[java] view plain copy print?
  1. publicclass Zoo {  
  2.     public Animal animal;  
  3.     public Zoo() { }  
  4. }  
  5. abstractclass Animal {  
  6.     public String name;  
  7.     public Animal() { }  
  8. }  
  9. class Cat extends
     Animal {  
  10.     publicint lives;  
  11.     public Cat() { }  
  12. }  
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字串反序列化為Zoo物件時,卻拋了“

JsonMappingException: Can not construct instance of” 異常。程式碼:

[java] view plain copy print?
  1. @Test(expected = JsonMappingException.class)  
  2. publicvoid givenAbstractClass_whenDeserializing_thenException()   
  3.   throws IOException {  
  4.     String json = ”{“animal“:{“name“:”lacy“}}”;  
  5.     ObjectMapper mapper = new ObjectMapper();  
  6.     mapper.reader().forType(Zoo.class).readValue(json);  
  7. }  
@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);
}
完整異常如下: [java] view plain copy print?
  1. com.fasterxml.jackson.databind.JsonMappingException:   
  2. Can not construct instance of org.baeldung.jackson.exception.Animal,  
  3.   problem: abstract types either need to be mapped to concrete types,   
  4.   have custom deserializer,   
  5.   or be instantiated with additional type information  
  6.   at   
  7. [Source: {”animal”:{“name”:“lacy”}}; line: 1, column: 2]   
  8. (through reference chain: org.baeldung.jackson.exception.Zoo[”animal”])  
  9.     at c.f.j.d.JsonMappingException.from(JsonMappingException.java:148)  
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. The Solution

可以通過在抽象類上新增@JsonDeserialize註解解決:

[java] view plain copy print?
  1. @JsonDeserialize(as = Cat.class)  
  2. abstractclass Animal {…}  
@JsonDeserialize(as = Cat.class)
abstract class Animal {...}

3. JsonMappingException: No suitable constructor

3.1. The Problem

現在我們來看另外一個常見的異常JsonMappingException: No suitable constructor found for type.

Jackson無法訪問建構函式的時候回拋這個異常。

這裡給出一個例子 –User類沒有預設的構造方法:

[java] view plain copy print?
  1. publicclass User {  
  2.     publicint id;  
  3.     public String name;  
  4.     public User(int id, String name) {  
  5.         this.id = id;  
  6.         this.name = name;  
  7.     }  
  8. }  
public class User {
    public int id;
    public String name;

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

當我們試圖將一個JSON字串反序列化為User物件時,卻拋異常 “JsonMappingException: No suitable constructor found” ,程式碼如下:

[java] view plain copy print?
  1. @Test(expected = JsonMappingException.class)  
  2. publicvoid givenNoDefaultConstructor_whenDeserializing_thenException()   
  3.   throws IOException {  
  4.     String json = ”{“id“:1,”name“:”John“}”;  
  5.     ObjectMapper mapper = new ObjectMapper();  
  6.     mapper.reader().forType(User.class).readValue(json);  
  7. }  
@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);
}

完整異常資訊:

[java] view plain copy print?
  1. com.fasterxml.jackson.databind.JsonMappingException:   
  2. No suitable constructor found for type  
  3. [simple type, class org.baeldung.jackson.exception.User]:  
  4.  can not instantiate from JSON object (need to add/enable type information?)  
  5.  at [Source: {”id”:1,“name”:“John”}; line: 1, column: 2]  
  6.         at c.f.j.d.JsonMappingException.from(JsonMappingException.java:148)  
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

解決這個問題只需要在類中新增一個預設的建構函式即可:

[java] view plain copy print?
  1. publicclass User {  
  2.     publicint id;  
  3.     public String name;  
  4.     public User() {  
  5.         super();  
  6.     }  
  7.     public User(int id, String name) {  
  8.         this.id = id;  
  9.         this.name = name;  
  10.     }  
  11. }  
public class User {
    public int id;
    public String name;

    public User() {
        super();
    }

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

這樣我們再反序列化的時候就正常的反序列化了:

[java] view plain copy print?
  1. @Test
  2. publicvoid givenDefaultConstructor_whenDeserializing_thenCorrect()   
  3.   throws IOException {  
  4.     String json = ”{“id“:1,”name“:”John“}”;  
  5.     ObjectMapper mapper = new ObjectMapper();  
  6.     User user = mapper.reader()  
  7.       .forType(User.class).readValue(json);  
  8.     assertEquals(”John”, user.name);  
  9. }  
@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.1. The Problem

接下來我們來看JsonMappingException: Root name does not match expected.

這個異常是由於JSON字串和Jackson找到的類不匹配導致的,例如下面的JSON字串反序列化為User時,會拋此異常:

[java] view plain copy print?
  1. @Test(expected = JsonMappingException.class)  
  2. publicvoid givenWrappedJsonString_whenDeserializing_thenException()  
  3.   throws IOException {  
  4.     String json = ”{“user“:{“id“:1,”name“:”John“}}”;  
  5.     ObjectMapper mapper = new ObjectMapper();  
  6.     mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);  
  7.     mapper.reader().forType(User.class).readValue(json);  
  8. }  
@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);
}

完整異常:

[java] view plain copy print?
  1. com.fasterxml.jackson.databind.JsonMappingException:  
  2. Root name ’user’ does not match expected (‘User’for type  
  3.  [simple type, class org.baeldung.jackson.dtos.User]  
  4.  at [Source: {”user”:{“id”:1,“name”:“John”}}; line: 1, column: 2]  
  5.    at c.f.j.d.JsonMappingException.from(JsonMappingException.java:148)  
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

我們可以通過@JsonRootName註解來解決這個問題,如下:

[java] view plain copy print?
  1. @JsonRootName(value = “user”)  
  2. publicclass UserWithRoot {  
  3.     publicint id;  
  4.     public String name;  
  5. }  
@JsonRootName(value = "user")
public class UserWithRoot {
    public int id;
    public String name;
}

這樣我們再執行反序列化時就能正常運行了:

[java] view plain copy print?
  1. @Test
  2. publicvoid
  3.   givenWrappedJsonStringAndConfigureClass_whenDeserializing_thenCorrect()   
  4.   throws IOException {  
  5.     String json = ”{“user“:{“id“:1,”name“:”John“}}”;  
  6.     ObjectMapper mapper = new ObjectMapper();  
  7.     mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);  
  8.     UserWithRoot user = mapper.reader()  
  9.       .forType(UserWithRoot.class)  
  10.       .readValue(json);  
  11.     assertEquals(”John”, user.name);  
  12. }  
@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方法都是private修飾的,也就是私有的,Jackson不能訪問到。

例如– 我們想序列化一個 “UserWithPrivateFields“物件:

[java] view plain copy print?
  1. publicclass UserWithPrivateFields {  
  2.     int id;  
  3.     String name;  
  4. }  
public class UserWithPrivateFields {
    int id;
    String name;
}
當我們序列化 “UserWithPrivateFields”的一個例項時 – 會丟擲異常 “JsonMappingException: No serializer found for class” :[java] view plain copy print?
  1. @Test(expected = JsonMappingException.class)  
  2. publicvoid givenClassWithPrivateFields_whenSerializing_thenException()   
  3.   throws IOException {  
  4.     UserWithPrivateFields user = new UserWithPrivateFields(1“John”);  
  5.     ObjectMapper mapper = new ObjectMapper();  
  6.     mapper.writer().writeValueAsString(user);  
  7. }  
@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);
}
完整異常:[java] view plain copy print?
  1. com.fasterxml.jackson.databind.JsonMappingException:   
  2. No serializer found forclass org.baeldung.jackson.exception.UserWithPrivateFields  
  3.  and no properties discovered to create BeanSerializer   
  4. (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) )  
  5.   at c.f.j.d.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:59)  
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的可見性來解決這個問題,例如:

[java] view plain copy print?
  1. @Test
  2. publicvoid givenClassWithPrivateFields_whenConfigureSerializing_thenCorrect()   
  3.   throws IOException {  
  4.     UserWithPrivateFields user = new UserWithPrivateFields(1“John”);  
  5.     ObjectMapper mapper = new ObjectMapper();  
  6.     mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);  
  7.     String result = mapper.writer().writeValueAsString(user);  
  8.     assertThat(result, containsString(”John”));  
  9. }  
@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註解解決:[java] view plain copy print?
  1. @JsonAutoDetect(fieldVisibility = Visibility.ANY)  
  2. publicclass UserWithPrivateFields { … }  
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class UserWithPrivateFields { ... }
當然,如果如果我們可以修改該實體類的原始碼,也可以通過新增getter方法解決,這個方法應該是首選的方法。

6. JsonMappingException: Can not deserialize instance of

6.1. The Problem

接下來看異常JsonMappingException: Can not deserialize instance of.

這個異常是由於在反序列化時型別使用不當導致的,例如我們想反序列化一個List<User>物件:

[java] view plain copy print?
  1. @Test(expected = JsonMappingException.class)  
  2. publicvoid givenJsonOfArray_whenDeserializing_thenException()   
  3.   throws JsonProcessingException, IOException {  
  4.     String json   
  5.       = ”[{“id“:1,”name“:”John“},{“id“:2,”name“:”Adam“}]”;  
  6.     ObjectMapper mapper = new ObjectMapper();  
  7.     mapper.reader().forType(User.class).readValue(json);  
  8. }  
@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);
}
完整異常:[java] view plain copy print?
  1. com.fasterxml.jackson.databind.JsonMappingException:  
  2. Can not deserialize instance of   
  3.   org.baeldung.jackson.dtos.User out of START_ARRAY token  
  4.   at [Source: [{”id”:1,“name”:“John”},{“id”:2,“name”:“Adam”}]; line: 1, column: 1]  
  5.   at c.f.j.d.JsonMappingException.from(JsonMappingException.java:148)  
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

可以通過更改UserList<User> –例如:

[java] view plain copy print?
  1. @Test
  2. publicvoid givenJsonOfArray_whenDeserializing_thenCorrect()   
  3.   throws JsonProcessingException, IOException {  
  4.     String json  
  5.       = ”[{“id“:1,”name“:”John“},{“id“:2,”name“:”Adam“}]”;  
  6.     ObjectMapper mapper = new ObjectMapper();  
  7.     List<User> users = mapper.reader()  
  8.       .forType(new TypeReference<List<User>>() {})  
  9.       .readValue(json);  
  10.     assertEquals(2, users.size());  
  11. }  
@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.1. The Problem

接下來我們來看UnrecognizedPropertyException.

此異常是由於我們在反序列化時,JSON字串中出現了未知的屬性導致的,比如我們想反序列化一個JSON,此JSON中帶有一個額外的屬性checked:

[java] view plain copy print?
  1. @Test(expected = UnrecognizedPropertyException.class)  
  2. publicvoid givenJsonStringWithExtra_whenDeserializing_thenException()   
  3.   throws IOException {  
  4.     String json = ”{“id“:1,”name“:”John“, ”checked“:true}”;  
  5.     ObjectMapper mapper = new ObjectMapper();  
  6.     mapper.reader().forType(User.class).readValue(json);  
  7. }  
@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);
}
完整異常:[java] view plain copy print?
  1. com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:  
  2. Unrecognized field ”checked” (class org.baeldung.jackson.dtos.User),  
  3.  not marked as ignorable (2 known properties: “id”“name”])  
  4.  at [Source: {”id”:1,“name”:“John”“checked”:true}; line: 1, column: 38]  
  5.  (through reference chain: org.baeldung.jackson.dtos.User[”checked”])  
  6.   at c.f.j.d.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:51)  
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

我們可以通過下面的方法配置ObjectMapper物件來解決這個問題:

[java] view plain copy print?
  1. @Test
  2. publicvoid givenJsonStringWithExtra_whenConfigureDeserializing_thenCorrect()   
  3.   throws IOException {  
  4.     String json = ”{“id“:1,”name“:”John“, ”checked“:true}”;  
  5.     ObjectMapper mapper = new ObjectMapper();  
  6.     mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);  
  7.     User user = mapper.reader().forType(User.class).readValue(json);  
  8.     assertEquals(”John”, user.name);  
  9. }  
@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註解來解決,可以忽略未知的屬性:[java] view plain copy print?
  1. @JsonIgnoreProperties(ignoreUnknown = true)  
  2. publicclass User {…}  
@JsonIgnoreProperties(ignoreUnknown = true)
public class User {...}

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

8.1. The Problem

接下來我們討論JsonParseException: Unexpected character (”’ (code 39)).

當我們反序列化的JSON中包含了單引號而不是雙引號的時候,會拋此異常,比如:

[java] view plain copy print?
  1. @Test(expected = JsonParseException.class)  
  2. publicvoid givenStringWithSingleQuotes_whenDeserializing_thenException()   
  3.   throws JsonProcessingException, IOException {  
  4.     String json = ”{‘id’:1,’name’:’John’}”;  
  5.     ObjectMapper mapper = new ObjectMapper();  
  6.     mapper.reader()  
  7.       .forType(User.class).readValue(json);  
  8. }  
@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);
}
完整異常:[java] view plain copy print?
  1. com.fasterxml.jackson.core.JsonParseException:  
  2. Unexpected character (‘ (code 39)):   
  3.   was expecting double-quote to start field name  
  4.   at [Source: {’id’:1,‘name’:‘John’}; line: 1, column: 3]  
  5.   at c.f.j.core.JsonParser._constructError(JsonParser.java:1419)  
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

我們也可以通過配置ObjectMapper 來實現對單引號的相容:

[java] view plain copy print?
  1. @Test
  2. publicvoid
  3.   givenStringWithSingleQuotes_whenConfigureDeserializing_thenCorrect()   
  4.   throws JsonProcessingException, IOException {  
  5.     String json = ”{‘id’:1,’name’:’John’}”;  
  6.     JsonFactory factory = new JsonFactory();  
  7.     factory.enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES);  
  8.     ObjectMapper mapper = new ObjectMapper(factory);  
  9.     User user = mapper.reader().forType(User.class)  
  10.       .readValue(json);  
  11.     assertEquals(”John”, user.name);  
  12. }  
@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

接下來我們快速的討論一下 “No such method” errors.

當拋了java.lang.NoSuchMethodError 異常時, 通常是因為你又多個Jackson的jar包導致的。不只是Jackson,其他的時候此異常也有可能是因為jar包的衝突導致。

完整的異常:

[java] view plain copy print?