Tuesday, September 10, 2024

How to Ignore Unknown Properties While Parsing JSON in Java? Example

One of the common problems while parsing JSON in Java using Jackson API is that it fails when your JSON contains unknown properties i.e. your Java class doesn't have all the fields corresponding to all JSON properties. For example, if you are consuming JSON from a REST Web Service and tomorrow they added a new field into JSON then your code will break because Jackson will throw UnrecognizedPropertyException and stop parsing JSON. This is troublesome and can cause problems in production if you are not aware. I  have faced this issue when a developer shipped the code to consume data from REST API without properly handling unknown fields.

The code worked fine for months but it broke as soon as the source system added a new field is added to REST API. The developer chooses to ignore the update because we weren't interested in that field but he failed to foresee that it will impact the JSON parsing.

Anyway, it was our fault that we didn't review the code properly and allowed him to release his code into production without handling unknown files. The issue could have simply been avoided if he was familiar with Jackson library in a little bit more detail.

Jackson API provides two ways to ignore unknown fields, first at the class level using @JsonIgnoreProperties annotation and second at the ObjectMapper level using configure() method.

You will see both approaches in this article and learn how to use them and when to use @JsonIgnoreProperties and when to ignore unknown fields in JSON globally at the ObjectMapper level.

Btw, if you are learning Java then I suggest you first go through these free Java online courses to build the foundation.




Ignoring unknown properties using @JsonIgnoreProperties

If you are creating a Model class to represent the JSON in Java, then you can annotate the class with @JsonIgnoreProperties(ignoreUnknown = true) to ignore any unknown field. 

This means if there is a new field is added tomorrow on JSON which represents your Model then Jackson will not throw UnrecognizedPropertyException while parsing JSON in Java. 

You can use this approach if you want to ignore unknown properties only for that Model class, but this is the preferred approach because it provides you more control.

Let's see an example of using @JsonIgnoreProperties in Java:

Suppose I have the following JSON, which represents my favorite book, Effective Java 3rd Edition, a must-read book for every Java developer, and a Java model class in my project:

How to ignore unknown properties while parsing JSON in Java


If tomorrow, I add a new field called "edition" in the JSON then parsing of this JSON will fail with the UnrecognizedPropertyException error. Something like :

Exception in thread "main" com.fasterxml.jackson.databind.exc
.UnrecognizedPropertyException: Unrecognized field "edition" 
(class EBook), not marked as ignorable 
(3 known properties: , "title", "price", "author"])"

This means Jackson is not able to find any field in your EBook class for the "edition" property in JSON and hence it's throwing the UnrecognizedPropertyException error.

You can solve this problem and prevent this error by using @JsonIgnoreProperties annotation as shown below:

@JsonIgnoreProperties(ignoreUnknown = true)
class EBook{
  private String title;
  private String author;
  private int price; 
  ..

}

We have just annotated a whole model class as @JsonIgnoreProperties(ignoreUnknown = true), which mean any unknown property in JSON String i.e. any property for which we don't have a corresponding field in the EBook class will be ignored. If you compile and run your program again it will work fine.

In Jackson 2.x, the @JsonIgnoreProperties reside in com.fasterxml.jackson.annotation package, hence you need to import it as :

import com.fasterxml.jackson.annotation.JsonIgnoreProperties.

If you are using an older version of Jackson API e.g. Jackson 1.x then this annotation belongs to a different package, beware of that, especially if you have both Jackson 1.x and Jackson 2.x in your classpath.



Ignoring Unknown Property in JSON Globally using Jackson

Another way to deal with unknown properties in JSON you are parsing is to configure ObjectMapper not to fail when it encounters an unknown property. This will also solve the problem of UnrecognizedPropertyException

You can enable this setting by calling configure() method as shown below:

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);


This will now ignore unknown properties for any JSON it's going to parse, You should only use this option if you can't annotate a class with @JsonIgnoreProperties annotation.

Btw, if you are not familiar with JSON parsing libraries in Java, then JSON with Java APIs, jQuery, and REST Web Services on Udemy is a good place to start with.

Jackson @JsonIgnoreProperties Annotation Example



Java Program to Ignore Unknown Properties while Parsing JSON using Jackson

Let's see whatever we have learned so far in action. Btw, if you are confused with my String JSON and a lot of "/r/n" string then don't worry. I haven't done that manually. I used this Eclipse trick to copy my JSON and it automatically included necessary escape characters. 

This is required because JSON string is enclosed with double quotes ("") which need to be escaped in Java.

Btw, if you are new to Eclipse IDE, then I suggest you check Beginners Eclipse Java IDE Training Course on Udemy to learn it well. It's important for Java developer to know their tools well, especially IDE so that they effectively develop, test, debug, and run their Java application.

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;

/*
 * Java Program to iterate over JSONObject of json-simple
 */
public class JacksonTest {

  private static String json = "{\r\n" + "\"title\" : \"Effective Java\",\r\n"
      + "\"author\" : \"Joshua Bloch\",\r\n" + "\"price\" : 37,\r\n"
      + "\"edition\" : 37\r\n" + "}";

  public static void main(String args[]) throws IOException {

    // let's parse JSON with a date field
    ObjectMapper mapper = new ObjectMapper();
    // mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
    // false);

    EBook effectiveJava = mapper.readValue(json, EBook.class);
    System.out.println("Input json string");
    System.out.println(json);
    System.out.println("Generated java class: ");
    System.out.println(effectiveJava);

  }

}

class EBook {
  private String title;
  private String author;
  private int price;

  public EBook() {
    // no argument constructor required by Jackson
  }

  public EBook(String title, String author, int price) {
    this.title = title;
    this.author = author;
    this.price = price;
  }

  public String getTitle() {
    return title;
  }

  public String getAuthor() {
    return author;
  }

  public int getPrice() {
    return price;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public void setAuthor(String author) {
    this.author = author;
  }

  public void setPrice(int price) {
    this.price = price;
  }

  @Override
  public String toString() {
    return "EBook [title=" + title + ", author=" + author + ", price=" + price
        + "]";
  }

}

Output:
Input json string
{
"title" : "Effective Java",
"author" : "Joshua Bloch",
"price" : 37,
"version" : 37
}
Generated java class: 
EBook [title=Effective Java, author=Joshua Bloch, price=37]

In this program, I have a JSON as discussed above which represents the Effective Java 3rd edition book, a must-read for every Java developer.

How to deal with unknown properties while parsing JSON in Java


I also have a model class called EBook, which is annotated with @JsonIgnoreProperties(ignoreUnknown = true) to ignore unknown properties.

If you look closely, our JSON String contains an "edition" property that is not defined in the Java class but the program works because we have marked EBook with

@JsonIgnoreProperties(ignoreUnknown = true) annotation.

If you want to test this program, then just remove that annotation and run the program, it will throw the following error.

Exception in thread "main" com.fasterxml.jackson.databind.exc
.UnrecognizedPropertyException: Unrecognized field
 "edition" (class EBook), not marked as ignorable
(3 known properties: , "title", "price", "author"])
at [Source: java.io.StringReader@19dfb72a; line: 5, column: 14] 
(through reference chain: EBook["edition"])
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 JacksonTest.main(JacksonTest.java:30)

This happened because of the "edition" field which is only present in JSON and not in the Java class. If you put the annotation back then the code will work again.

You can similarly test how to ignore unknown fields at the object mapper level, instead of putting the @JsonIgnoreProperties annotation back, you just uncomment the mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) line in the code and run the program. This time also it will work because Jackson is ignoring all unknown properties.

Also here is a nice Jackson annotation cheat sheet to revise what these annotation do:

Jackson annotation cheat sheet

essential Jackson annotations with examples




That's all about how to ignore unknown properties while parsing JSON in Java using Jackson API. You can do this either by using @JsonIgnoreProperties annotation or configuring ObjectMapper to not fail when encountering unknown properties during deserialization by disabling FAIL_ON_UNKNOWN_PROPERTIES.

Though, the preferred approach is to ignore unknown properties at the class level using @JsonIgnoreProperties(ignoreUnknown = true) and only do this on the ObjectMapper level if you can't annotate your class with this annotation i.e. you don't own the class. 

It's also a best practice to annotated your model class with @JsonIgnoreProperties to avoid the issue I have explained in the first paragraph.

Other Java and JSON resources you may like
How to parse JSON using Gson?
5 JSON parsing libraries Java Developers Should Know
How to parse JSON array in Java?
How to convert JSON to HashMap in Java?
10 Things Java developers should learn
How to parse JSON with Date field in Java?

Thanks for reading this article so far. If you like this article then please share it with your friends and colleagues. If you have any questions or doubts, please drop a note.

2 comments:

  1. what if there is a property in a Java object but the json does not have corresponding field? Will jackson ignore the field while deserialization? will it set null value for that field in Java object?

    ReplyDelete
  2. Yes, those will be initialized with default value. So if that property is an object then it will be null, if its boolean then it will be false and if its an integer then it will be zero. It's just like a member variable initialized with their default value.

    ReplyDelete