Jackson: What happens if a property is missing?

JavaJackson

Java Problem Overview


What happens if I annotate a constructor parameter using @JsonProperty but the Json doesn't specify that property. What value does the constructor get?

How do I differentiate between a property having a null value versus a property that is not present in the JSON?

Java Solutions


Solution 1 - Java

Summarizing excellent answers by Programmer Bruce and StaxMan:

  1. Missing properties referenced by the constructor are assigned a default value as defined by Java.

  2. You can use setter methods to differentiate between properties that are implicitly or explicitly set. Setter methods are only invoked for properties with explicit values. Setter methods can keep track of whether a property was explicitly set using a boolean flag (e.g. isValueSet).

Solution 2 - Java

> What happens if I annotate a constructor parameter using @JsonProperty but the Json doesn't specify that property. What value does the constructor get?

For questions such as this, I like to just write a sample program and see what happens.

Following is such a sample program.

import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.ObjectMapper;

public class JacksonFoo
{
  public static void main(String[] args) throws Exception
  {
    ObjectMapper mapper = new ObjectMapper();
    
    // {"name":"Fred","id":42}
    String jsonInput1 = "{\"name\":\"Fred\",\"id\":42}";
    Bar bar1 = mapper.readValue(jsonInput1, Bar.class);
    System.out.println(bar1);
    // output: 
    // Bar: name=Fred, id=42
    
    // {"name":"James"}
    String jsonInput2 = "{\"name\":\"James\"}";
    Bar bar2 = mapper.readValue(jsonInput2, Bar.class);
    System.out.println(bar2);
    // output:
    // Bar: name=James, id=0
    
    // {"id":7}
    String jsonInput3 = "{\"id\":7}";
    Bar bar3 = mapper.readValue(jsonInput3, Bar.class);
    System.out.println(bar3);
    // output:
    // Bar: name=null, id=7
  }
}

class Bar
{
  private String name = "BLANK";
  private int id = -1;
  
  Bar(@JsonProperty("name") String n, @JsonProperty("id") int i)
  {
    name = n;
    id = i;
  }
  
  @Override
  public String toString()
  {
    return String.format("Bar: name=%s, id=%d", name, id);
  }
}

The result is that the constructor is passed the default value for the data type.

> How do I differentiate between a property having a null value versus a property that is not present in the JSON?

One simple approach would be to check for a default value post deserialization processing, since if the element were present in the JSON but had a null value, then the null value would be used to replace any default value given the corresponding Java field. For example:

import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
import org.codehaus.jackson.annotate.JsonMethod;
import org.codehaus.jackson.map.ObjectMapper;

public class JacksonFooToo
{
  public static void main(String[] args) throws Exception
  {
    ObjectMapper mapper = new ObjectMapper().setVisibility(JsonMethod.FIELD, Visibility.ANY);
    
    // {"name":null,"id":99}
    String jsonInput1 = "{\"name\":null,\"id\":99}";
    BarToo barToo1 = mapper.readValue(jsonInput1, BarToo.class);
    System.out.println(barToo1);
    // output:
    // BarToo: name=null, id=99
    
    // {"id":99}
    String jsonInput2 = "{\"id\":99}";
    BarToo barToo2 = mapper.readValue(jsonInput2, BarToo.class);
    System.out.println(barToo2);
    // output:
    // BarToo: name=BLANK, id=99

    // Interrogate barToo1 and barToo2 for 
    // the current value of the name field.
    // If it's null, then it was null in the JSON.
    // If it's BLANK, then it was missing in the JSON.
  }
}

class BarToo
{
  String name = "BLANK";
  int id = -1;
  
  @Override
  public String toString()
  {
    return String.format("BarToo: name=%s, id=%d", name, id);
  }
}

Another approach would be to implement a custom deserializer that checks for the required JSON elements. And yet another approach would be to log an enhancement request with the Jackson project at http://jira.codehaus.org/browse/JACKSON

Solution 3 - Java

In addition to constructor behavior explained in @Programmer_Bruce's answer, one way to differentiate between null value and missing value is to define a setter: setter is only called with explicit null value. Custom setter can then set a private boolean flag ("isValueSet" or whatever) if you want to keep track of values set.

Setters have precedence over fields, in case both field and setter exist, so you can "override" behavior this way as well.

Solution 4 - Java

I'm thinking of using something in the style of an Option class, where a Nothing object would tell me if there is such a value or not. Has anyone done something like this with Jackson (in Java, not Scala, et al)?

Solution 5 - Java

(My answer might be useful to some people finding this thread via google, even if it doesn't answer OPs question)
If you are dealing with primitive types which are omittable, and you do not want to use a setter like described in the other answers (for example if you want your field to be final), you can use box objects:

public class Foo {
    private final int number;
    public Foo(@JsonProperty Integer number) {
        if (number == null) {
            this.number = 42; // some default value
        } else {
            this.number = number;
        }
    }
}

this doesn't work if the JSON actually contains null, but it can be sufficient if you know it will only contain primitives or be absent

Solution 6 - Java

another option is to validate the object after deserialization either manually or via frameworks such java bean validation or, if you are using spring, the spring validation support.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGiliView Question on Stackoverflow
Solution 1 - JavaGiliView Answer on Stackoverflow
Solution 2 - JavaProgrammer BruceView Answer on Stackoverflow
Solution 3 - JavaStaxManView Answer on Stackoverflow
Solution 4 - JavaRobertoView Answer on Stackoverflow
Solution 5 - JavaFelkView Answer on Stackoverflow
Solution 6 - JavaabdelView Answer on Stackoverflow