转载自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的对象:
- 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() { }
- }
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” 异常。代码:
- @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);
- }
@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)
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注解解决:
- @JsonDeserialize(as = Cat.class)
- abstract class 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类没有默认的构造方法:
- public class User {
- public int 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” ,代码如下:
- @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);
- }
@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)
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
解决这个问题只需要在类中添加一个默认的构造函数即可:
- public class User {
- public int 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; } }
这样我们再反序列化的时候就正常的反序列化了:
- @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 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时,会抛此异常:
- @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);
- }
@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)
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注解来解决这个问题,如下:
- @JsonRootName(value = “user”)
- public class UserWithRoot {
- public int id;
- public String name;
- }
@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);
- }
@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“对象:
- public class UserWithPrivateFields {
- int id;
- String name;
- }
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);
- }
@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)
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”));
- }
@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 { … }
@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>对象:
- @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);
- }
@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:
- 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> –例如:
- @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());
- }
@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:
- @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);
- }
@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)
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对象来解决这个问题:
- @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);
- }
@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注解来解决,可以忽略未知的属性:
- @JsonIgnoreProperties(ignoreUnknown = true)
- public class User {…}
@JsonIgnoreProperties(ignoreUnknown = true) public class User {...}
8. JsonParseException: Unexpected character (”’ (code 39))
8.1. The Problem
接下来我们讨论JsonParseException: Unexpected character (”’ (code 39)).
当我们反序列化的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);
- }
@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); }完整异常:
- 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 来实现对单引号的兼容:
- @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);
- }
@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.lang.NoSuchMethodError:
- com.fasterxml.jackson.core.JsonParser.getValueAsString()Ljava/lang/String;
- at c.f.j.d.deser.std.StringDeserializer.deserialize(StringDeserializer.java:24)
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
在这篇文章中,我们深一层解析了常见的Jackson中的异常和错误,并且对每一个异常和错误分析了原因和解决方案。以上所有的代码都可以在Github上找到,这是一个基于Maven的工程,很容易就可以导入并且运行拉。