杰克逊: 如果财产丢失了怎么办?

如果我使用 @JsonProperty注释构造函数参数,但 Json 没有指定该属性,会发生什么情况。构造函数得到什么值?

如何区分具有 null 值的属性和不存在于 JSON 中的属性?

90487 次浏览

如果我使用@JsonProperty 对构造函数参数进行注释,但 Json 没有指定该属性,会发生什么情况。构造函数得到什么值?

对于这样的问题,我喜欢只编写一个示例程序,看看会发生什么。

下面是这样一个示例程序。

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);
}
}

结果是将数据类型的默认值传递给构造函数。

如何区分具有 null 值的属性和不存在于 JSON 中的属性?

一个简单的方法是在反序列化处理之后检查默认值,因为如果元素存在于 JSON 中但是有一个 null 值,那么 null 值将被用来替换给定相应 Java 字段的任何默认值。例如:

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);
}
}

另一种方法是实现一个自定义反序列化器来检查所需的 JSON 元素。还有一种方法是在 http://jira.codehaus.org/browse/JACKSON上记录 Jackson 项目的增强请求

除了在@Programmer _ Bruce 的回答中解释的构造函数行为之外,区分 null 值和丢失值的一种方法是定义 setter: setter 只使用显式 null 值调用。 自定义 setter 可以设置一个私有布尔标志(“ isValueSet”或其他) ,如果您想跟踪设置的值。

如果字段和 setter 都存在,则 setter 优先于字段,因此您也可以通过这种方式“覆盖”行为。

总结 程序员布鲁斯斯塔克斯曼的优秀答案:

  1. 构造函数引用的缺少属性被分配一个默认值 由 Java 定义

  2. 可以使用 setter 方法区分隐式或显式设置的属性。Setter 方法仅对具有显式值的属性进行调用。Setter 方法可以跟踪是否使用布尔标志显式设置了属性(例如 isValueSet)。

我正在考虑使用 Option 类的样式,其中 Nothing 对象将告诉我是否存在这样的值。有人对 Jackson 做过类似的事情吗(在 Java 中,不是 Scala,等等) ?

(我的答案可能对一些人通过谷歌找到这个帖子很有用,即使它没有回答操作系统的问题)
如果你正在处理可省略的基本类型,并且你不想使用其他答案中描述的 setter 类型(例如,如果你希望你的字段是 final 类型) ,你可以使用 box 对象:

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;
}
}
}

如果 JSON 实际上包含 null,那么这不起作用,但是如果您知道它只包含原语或者不包含原语,那么这就足够了

另一种选择是在反序列化之后将对象 确认变成手动或通过诸如 java bean 验证之类的框架,或者,如果您使用 spring,则使用 Spring 验证支持。