【问题标题】:Jackson: What happens if a property is missing?杰克逊:如果财产丢失怎么办?
【发布时间】:2012-01-09 09:31:12
【问题描述】:

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

如何区分具有空值的属性与 JSON 中不存在的属性?

【问题讨论】:

标签: java jackson


【解决方案1】:

如果我使用 @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);
  }
}

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

如何区分具有空值的属性与 JSON 中不存在的属性?

一种简单的方法是在反序列化处理后检查默认值,因为如果元素存在于 JSON 中但具有空值,那么空值将用于替换给定相应 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 项目中记录增强请求

【讨论】:

  • “证据在布丁中”。我同意,有什么比尝试更好的学习方式。
  • 那个项目链接失效了。
  • 有关实现自定义反序列化程序的任何信息,该反序列化程序可以检查不存在的属性并根据预期类型分配默认值?
  • 这个方法的问题是你必须使用布尔数据类型,因为我们不能设置随机值,除了真或假
  • > 对于这样的问题,我喜欢只写一个示例程序,看看会发生什么。 — 这并不总是一个好主意,例如,库的当前行为可能会在文档中明确标记为不稳定。如果可能,最好检查文档。
【解决方案2】:

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

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

【讨论】:

  • 假设你使用同一个类进行序列化和反序列化。如果isValueSet 为假,你如何告诉序列化程序省略属性value
  • 这需要自定义序列化程序,无法指定排序保护标志。尽管显然可以使用基于注释的方法来实现这样的扩展。
【解决方案3】:

总结Programmer BruceStaxMan的优秀答案:

  1. 构造函数引用的缺失属性被分配一个默认值as defined by Java

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

【讨论】:

    【解决方案4】:

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

    【讨论】:

    • 我正在使用 Guava 的 Optional 作为可选属性。如果 JSON 中的字段缺失,则不会分配该属性。我已经通过使用Optional.absent() 初始化字段解决了这个问题,但这意味着我不能使用final 字段,这有点限制。
    【解决方案5】:

    (我的回答可能对某些通过谷歌找到这个帖子的人有用,即使它没有回答 OP 问题)
    如果您正在处理可省略的原始类型,并且您不想使用其他答案中描述的设置器(例如,如果您希望您的字段是最终的),您可以使用框对象:

    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,则此方法不起作用,但如果您知道它仅包含原语或不包含,则它就足够了

    【讨论】:

      【解决方案6】:

      另一种选择是在反序列化后手动或通过框架(如 java bean 验证)验证对象,或者,如果您使用的是 spring,则支持 spring 验证。

      【讨论】:

      • 这不起作用。它不允许您区分具有空值的属性与 JSON 中不存在的属性。
      • 我认为,如果该字段在域/dto 中可以为空,那么没关系,缺少的 json 字段和具有空值的当前 json 字段都将转换为空域字段。如果 domain 字段不可为 null ,则验证将捕获缺少 json 字段的两种情况,并呈现带有 null 值的 json。您可能还想使用@Jsoniignoreproperties(ignoreunknown) 来检测/失败遇到未知的 json 字段
      • 这很重要。我在过去实现了一个 REST API,其中未指定的可选属性被分配了默认值,但是明确设置为 null 的字段被相应地验证(例如,将 null 分配给布尔属性会导致错误) .同样,空字符串不等同于字符串的默认值。关键是要避免验证用户可以传入的每个可能的疯狂值并逐字解释他们的输入。这样就没有惊喜了。
      猜你喜欢
      • 2013-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-28
      • 2015-04-19
      • 1970-01-01
      相关资源
      最近更新 更多