Jackson異常問題和解決方案總結
轉載自https://blog.csdn.net/u010429286/article/details/78395775
在本教程中, 我們會回顧最常見的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?- publicclass Zoo {
- public Animal animal;
- public Zoo() { }
- }
- abstractclass Animal {
- public String name;
- public Animal() { }
- }
- class Cat extends
- publicint lives;
- public Cat() { }
- }
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物件時,卻拋了“
- @Test(expected = JsonMappingException.class)
- publicvoid givenAbstractClass_whenDeserializing_thenException()
- throws IOException {
- String json = ”{“animal“:{“name“:”lacy“}}”;
- ObjectMapper mapper = new ObjectMapper();
- mapper.reader().forType(Zoo.class).readValue(json);
- }
@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?- 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)
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?- @JsonDeserialize(as = Cat.class)
- 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?- publicclass User {
- publicint id;
- public String name;
- public User(int id, String name) {
- this.id = id;
- this.name = name;
- }
- }
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?- @Test(expected = JsonMappingException.class)
- publicvoid givenNoDefaultConstructor_whenDeserializing_thenException()
- throws IOException {
- String json = ”{“id“:1,”name“:”John“}”;
- ObjectMapper mapper = new ObjectMapper();
- mapper.reader().forType(User.class).readValue(json);
- }
@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?- 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)
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?- publicclass User {
- publicint id;
- public String name;
- public User() {
- super();
- }
- public User(int id, String name) {
- this.id = id;
- this.name = name;
- }
- }
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?- @Test
- publicvoid 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
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?- @Test(expected = JsonMappingException.class)
- publicvoid 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);
- }
@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?- 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)
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?- @JsonRootName(value = “user”)
- publicclass UserWithRoot {
- publicint id;
- public String name;
- }
@JsonRootName(value = "user")
public class UserWithRoot {
public int id;
public String name;
}
這樣我們再執行反序列化時就能正常運行了:
[java] view plain copy print?- @Test
- publicvoid
- 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);
- }
@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?- publicclass UserWithPrivateFields {
- int id;
- String name;
- }
public class UserWithPrivateFields {
int id;
String name;
}
當我們序列化 “UserWithPrivateFields”的一個例項時 – 會丟擲異常 “JsonMappingException: No serializer found for class” :[java] view plain copy print?- @Test(expected = JsonMappingException.class)
- publicvoid givenClassWithPrivateFields_whenSerializing_thenException()
- throws IOException {
- UserWithPrivateFields user = new UserWithPrivateFields(1, “John”);
- ObjectMapper mapper = new ObjectMapper();
- mapper.writer().writeValueAsString(user);
- }
@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?- com.fasterxml.jackson.databind.JsonMappingException:
- No serializer found forclass 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)
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?- @Test
- publicvoid 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”));
- }
@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?- @JsonAutoDetect(fieldVisibility = Visibility.ANY)
- 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?- @Test(expected = JsonMappingException.class)
- publicvoid 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);
- }
@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?- 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)
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
可以通過更改User 為List<User> –例如:
[java] view plain copy print?- @Test
- publicvoid 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());
- }
@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?- @Test(expected = UnrecognizedPropertyException.class)
- publicvoid givenJsonStringWithExtra_whenDeserializing_thenException()
- throws IOException {
- String json = ”{“id“:1,”name“:”John“, ”checked“:true}”;
- ObjectMapper mapper = new ObjectMapper();
- mapper.reader().forType(User.class).readValue(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);
}
完整異常:[java] view plain copy print?- 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)
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?- @Test
- publicvoid 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);
- }
@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?- @JsonIgnoreProperties(ignoreUnknown = true)
- 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?- @Test(expected = JsonParseException.class)
- publicvoid givenStringWithSingleQuotes_whenDeserializing_thenException()
- throws JsonProcessingException, IOException {
- String json = ”{‘id’:1,’name’:’John’}”;
- ObjectMapper mapper = new ObjectMapper();
- mapper.reader()
- .forType(User.class).readValue(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);
}
完整異常:[java] view plain copy print?- 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)
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?- @Test
- publicvoid
- 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);
- }
@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?