While parsing JSON string received from one of our RESTful web services, I was getting this error "Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "person" (class Hello$Person), not marked as ignorable". After some research, I found that this is one of the common errors while parsing JSON documents using Jackson open-source library in Java application. The error messages say that it is not able to find a suitable property name called "person" in our case, let's first take a look at the JSON we are trying to parse, the class we are using to represent the JSON document, and the error message itself.
Error Message:
The error messages say that it can find out id, city, name, and phone attributes in the Person class but is not able to locate the "person" field.
Our POJO class looks like below:
and the JSON String:
If you look carefully, the "person" field points to a JSON array and not an object, which means it cannot be mapped to the person class directly.
1) Configure Jackson's ObjectMapper to not fail when encounter unknown properties
You can do this by disabling DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES property of ObjectMapper as shown below:
Now, the error will go away but the Output is not what you expected, it will print the following:
Person [id=0, name=null, city=null, phone=0]
You can see that the Person class is not created properly, the relevant attributes are null even though the JSON String contains its value.
The reason was that JSON String contains a JSON array, the person field is pointing towards an array and there is no field corresponding to that in Person class.
In order to properly parse the JSON String we need to create a wrapper class Community which will have an attribute to keep an array of Person as shown below:
Now, we will convert the JSON String to this Community class and print each person from the list as shown below:
This will print the details of a person properly as shown below:
Now, coming back to a more general situation where a new field is added on JSON but not available in your Person class, let's see what happens.
Suppose, our JSON String to parse is the following:
When you run the same program with this JSON String, you will get the following error:
Again, Jackson is not able to recognize the new "facebook" property. Now, we can ignore this property by disabling the feature which tells Jackson to fail on the unknown property as shown below:
And this will print the person class properly as shown below:
Alternatively, you can also use @JsonIgnoreProperties annotation to ignore undeclared properties.
The @JsonIgnoreProperties is a class-level annotation in Jackson and it will ignore every property you haven't defined in your POJO. Very useful when you are just looking for a couple of properties in the JSON and don't want to write the whole mapping.
This annotation provides control at the class level i.e. you can tell Jackson that for this class, please ignore any attribute not defined by doing
So, our Person class now looks like this:
When I run first version of this program, I was greeted with the following error:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class Hello$Person]: can not instantiate from JSON object (need to add/enable type information?)
at [Source: java.io.StringReader@5e329ba8; line: 2, column: 3]
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:164)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:984)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:276)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2888)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2034)
at Hello.main(Hello.java:40)
This error was occurring because my nested class Person was not static, which means it cannot be instantiated because of having any Outer class instance. The issue was resolved after making the Person class static.
If you are not familiar with this detail before, I suggest you check Java Fundamentals: The Core Platform, a free course from Pluralsight to learn more about such details of Java programming language. You can signup for a free trial, which gives you 10 days access, enough to learn whole Java for free.
Now, let's see the real error:
When you run the final version of the program you will see the following output:
This means we are able to parse JSON containing unknown attributes successfully in Jackson.
You can simply copy-paste the code into your favorite IDE e.g. Eclipse to compile and run the program.
In Eclipse, you don't even need to create the class file because it will automatically create the class and package if you copy-paste the code in the Java project.
If Eclipse is your primary IDE and you want to learn more of such productivity tips I suggest you check out The Eclipse Guided Tour - Part 1 and 2 By Tod Gentille.
It's a free, online course to learn both basic and advanced features of Eclipse IDE, which every Java developer should be aware of. You can get access to this course by signing up for a free trial, which gives you 10 days of access to the whole Pluralsight library, one of the most valuable collections to learn about programming and other technology. Btw, 10 days is more than enough to learn Java and Eclipse together.
Anyway, once you copy-paste the code, all you need to do is either include Maven dependency in your pom.xml or manually download the required JAR file for Jackson open source library.
For Maven Users
You can add the following Maven dependency on your project's pom.xml and then run the mvn build or mvn install command to compile:
Error Message:
Exception in thread "main" com.fasterxml.jackson.databind.exc.
UnrecognizedPropertyException:
Unrecognized field "person" (class Hello$Person),
not marked as ignorable (4 known properties: , "id", "city", "name", "phone"])
The error messages say that it can find out id, city, name, and phone attributes in the Person class but is not able to locate the "person" field.
Our POJO class looks like below:
class Person{
private int id;
private String name;
private String city;
private long phone;
.....
}
and the JSON String:
{
"person": [
{
"id": "11",
"name": "John",
"city": "NewYork",
"phone": 7647388372
}
]
}
If you look carefully, the "person" field points to a JSON array and not an object, which means it cannot be mapped to the person class directly.
How to solve this problem?
Here are steps to solve this problem and get rid of this error:1) Configure Jackson's ObjectMapper to not fail when encounter unknown properties
You can do this by disabling DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES property of ObjectMapper as shown below:
// Jackson code to convert JSON String to Java object
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
Person p = objectMapper.readValue(JSON, Person.class);
System.out.println(p);
Now, the error will go away but the Output is not what you expected, it will print the following:
Person [id=0, name=null, city=null, phone=0]
You can see that the Person class is not created properly, the relevant attributes are null even though the JSON String contains its value.
The reason was that JSON String contains a JSON array, the person field is pointing towards an array and there is no field corresponding to that in Person class.
In order to properly parse the JSON String we need to create a wrapper class Community which will have an attribute to keep an array of Person as shown below:
static class Community { private List<Person> person; public List<Person> getPerson() { return person; } public void setPerson(List<Person> person) { this.person = person; } }
Now, we will convert the JSON String to this Community class and print each person from the list as shown below:
ObjectMapper objectMapper = new ObjectMapper();
//objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
Community c = objectMapper.readValue(JSON, Community.class);
for (Person p : c.getPerson()) {
System.out.println(p);
}
This will print the details of a person properly as shown below:
Person [id=11, name=John, city=NewYork, phone=7647388372]
Now, coming back to a more general situation where a new field is added on JSON but not available in your Person class, let's see what happens.
Suppose, our JSON String to parse is the following:
{
"person": [
{
"id": "11",
"name": "John",
"city": "NewYork",
"phone": 7647388372,
"facebook": "JohnTheGreat"
}
]
}
When you run the same program with this JSON String, you will get the following error:
Again, Jackson is not able to recognize the new "facebook" property. Now, we can ignore this property by disabling the feature which tells Jackson to fail on the unknown property as shown below:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
Community c = objectMapper.readValue(JSON, Community.class);
And this will print the person class properly as shown below:
Person [id=11, name=John, city=NewYork, phone=7647388372]
Alternatively, you can also use @JsonIgnoreProperties annotation to ignore undeclared properties.
The @JsonIgnoreProperties is a class-level annotation in Jackson and it will ignore every property you haven't defined in your POJO. Very useful when you are just looking for a couple of properties in the JSON and don't want to write the whole mapping.
This annotation provides control at the class level i.e. you can tell Jackson that for this class, please ignore any attribute not defined by doing
@JsonIgnoreProperties(ignoreUnknown = true)
So, our Person class now looks like this:
@JsonIgnoreProperties(ignoreUnknown = true)
static class Person{
private int id;
private String name;
private String city;
private long phone;
......
}
Sample program
import java.io.IOException; import java.util.List; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; /* * { "person": [ { "id": "11", "name": "John", "city": "NewYork", "phone": 7647388372 } ] } */ public class Hello { private static String JSON = "{\r\n" + " \"person\": [\r\n" + " {\r\n" + " \"id\": \"11\",\r\n" + " \"name\": \"John\",\r\n" + " \"city\": \"NewYork\",\r\n" + " \"phone\": 7647388372,\r\n" + " \"facebook\": \"JohnTheGreat\"\r\n" + " }\r\n" + " ]\r\n" + " } "; public static void main(String args[]) throws JsonParseException, JsonMappingException, IOException { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); Community c = objectMapper.readValue(JSON, Community.class); for (Person p : c.getPerson()) { System.out.println(p); } } static class Community { private List<Person> person; public List<Person> getPerson() { return person; } public void setPerson(List<Person> person) { this.person = person; } } static class Person { private int id; private String name; private String city; private long phone; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public long getPhone() { return phone; } public void setPhone(long phone) { this.phone = phone; } @Override public String toString() { return "Person [id=" + id + ", name=" + name + ", city=" + city + ", phone=" + phone + "]"; } } }
When I run first version of this program, I was greeted with the following error:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class Hello$Person]: can not instantiate from JSON object (need to add/enable type information?)
at [Source: java.io.StringReader@5e329ba8; line: 2, column: 3]
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:164)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:984)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:276)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2888)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2034)
at Hello.main(Hello.java:40)
This error was occurring because my nested class Person was not static, which means it cannot be instantiated because of having any Outer class instance. The issue was resolved after making the Person class static.
If you are not familiar with this detail before, I suggest you check Java Fundamentals: The Core Platform, a free course from Pluralsight to learn more about such details of Java programming language. You can signup for a free trial, which gives you 10 days access, enough to learn whole Java for free.
Now, let's see the real error:
Exception in thread "main" com.fasterxml.jackson.databind.exc.
UnrecognizedPropertyException: Unrecognized field "person" (class Hello$Person),
not marked as ignorable (4 known properties: , "id", "city", "name", "phone"])
at [Source: java.io.StringReader@4fbc9499; line: 2, column: 14]
(through reference chain: Person["person"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.
from(UnrecognizedPropertyException.java:79)
at com.fasterxml.jackson.databind.DeserializationContext.
reportUnknownProperty(DeserializationContext.java:555)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.
handleUnknownProperty(StdDeserializer.java:708)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.
handleUnknownProperty(BeanDeserializerBase.java:1160)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.
deserializeFromObject(BeanDeserializer.java:315)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.
deserialize(BeanDeserializer.java:121)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2888)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2034)
at Hello.main(Hello.java:40)
When you run the final version of the program you will see the following output:
Person [id=11, name=John, city=NewYork, phone=7647388372]
This means we are able to parse JSON containing unknown attributes successfully in Jackson.
How to compile and run this program?
You can simply copy-paste the code into your favorite IDE e.g. Eclipse to compile and run the program.In Eclipse, you don't even need to create the class file because it will automatically create the class and package if you copy-paste the code in the Java project.
If Eclipse is your primary IDE and you want to learn more of such productivity tips I suggest you check out The Eclipse Guided Tour - Part 1 and 2 By Tod Gentille.
It's a free, online course to learn both basic and advanced features of Eclipse IDE, which every Java developer should be aware of. You can get access to this course by signing up for a free trial, which gives you 10 days of access to the whole Pluralsight library, one of the most valuable collections to learn about programming and other technology. Btw, 10 days is more than enough to learn Java and Eclipse together.
Anyway, once you copy-paste the code, all you need to do is either include Maven dependency in your pom.xml or manually download the required JAR file for Jackson open source library.
For Maven Users
You can add the following Maven dependency on your project's pom.xml and then run the mvn build or mvn install command to compile:
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.2.3</version> </dependency>
This dependency requires jackson-core and jackson-annotations but Maven will automatically download that for you.
Manually Downloading JAR
If you are not using Maven or any other build tool e.g.gradle then you can just go to Maven central library and download the following three JAR files and include them in your classpath:
jackson-databind-2.2.3.jar
jackson-core-2.2.3.jar
jackson-annotations-2.2.3.jar
Once you compiled the class successfully you can run them as you run any other Java program in Eclipse, as shown here or you can run the JAR file using the command line as shown here.
In short, The "com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field XXX, not marked as ignorable" error comes when you try to parse JSON to a Java object which doesn't contain all the fields defined in JSON. You can solve this error by either disabling the feature of Jackson which tells it to fail if encounters unknown properties or by using annotation @JsonIgnoreProperties at the class level.
Other Java JSON Tutorials you may like:
- 3 Ways to parse JSON String in Java? (tutorial)
- How to use Google Protocol Buffer in Java? (tutorial)
- How to parse large JSON documents in Java? (code example)
- How to read JSON String using the json-simple library? (code example)
- How to convert JSON String to Java Object for example? (solution)
- How to convert a JSON array to String Array in Java? (solution)
- How to use Gson to convert JSON to Java Object? (example)
Thanks for reading this article so far. If you like my explanation then please share it with your friends and colleagues. If you have any questions or feedback, please drop a note.
Can u post the github link as well
ReplyDeleteI don't generally post the code on github but if you guys want, may be I can add.
ReplyDelete