【问题标题】:Excluding NullNode when serializing a Jackson JSON tree model序列化 Jackson JSON 树模型时排除 NullNode
【发布时间】:2014-02-02 14:05:41
【问题描述】:

我有一个 pojo 类型,当它被序列化时,它需要将特定的数值设置为一个特殊的字符串。这些值将始终为空,可能非常深入层次结构。

为了实现这一点,我首先将 pojo 转换为具有完整空值的 JsonNode 以保留属性顺序,然后我按照结构中的路径设置一些字符串并根据需要创建节点。最后,我让 ObjectMapper 将 JsonNode 序列化为字符串。逻辑看起来像这样:

ObjectMapper nonNullMapper = new ObjectMapper();
nonNullMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

ObjectMapper includeAllMapper = new ObjectMapper();
includeAllMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);

// NullNodes create stubs to maintain property order
JsonNode node = includeAllMapper.valueToTree(pojoInstance);
insertStrings(node);

String json = nonNullMapper.writeValueAsString(node); 
// Halp, there's still nulls

请注意,我知道有一个 @JsonInclude 注释,所以我实际上并不需要两个映射器,但事实证明我想序列化 pojo 实例而在其他地方没有空值,所以我不能将它用于我的知识。

无论如何,如何避免将 NullNodes 序列化到我的 json 字符串中?到目前为止,我发现了两种方法:

  1. 转换为 Map 然后序列化为字符串,禁用 SerializationFeature.WRITE_NULL_MAP_VALUES。这似乎效率低下而且很老套。
  2. 在序列化 JsonNode 之前手动删除 NullNode 实例。鉴于支持排除 pojos 和地图的空值,似乎没有必要这样做,并且它增加了(也许?)不必要的复杂性。

我尝试为NullNode 注册JsonSerializer,但它似乎没有被使用。我注意到NullNode 本身实现了JsonSerializable,它只是委托给SerializerProvider 的空值序列化程序。我犹豫是否要覆盖它,我觉得应该在值序列化之前进行空过滤,但我没有深入了解它的工作原理。

有没有更好的办法?

【问题讨论】:

    标签: java json jackson


    【解决方案1】:

    除了@StaxMan 的回答,这里有一些修剪 NullNode 的代码:

    public static void stripNulls(JsonNode node) {
        Iterator<JsonNode> it = node.iterator();
        while (it.hasNext()) {
            JsonNode child = it.next();
            if (child.isNull())
                it.remove();
            else
                stripNulls(child);
        }
    }
    

    【讨论】:

      【解决方案2】:

      我不知道一个好的解决方案,但只是想我指出 JSON 树模型 (JsonNode) 用于呈现确切的 JSON 结构;因此,很少有用于过滤或转换的一般特征被应用。非常接近所见即所得。

      因此,我可能宁愿只遍历树并明确修剪NullNodes。尝试配置 Jackson 本身来完成这项工作最终比显式删除节点要多。

      【讨论】:

        【解决方案3】:

        我猜您的问题是由于您尝试获取已序列化对象的 String 值。

        当您不序列化任何内容时,setSerializationInclusion 参数应该做什么(因为您已经使用不同的 ObjectMapper 对其进行了序列化)?

        您可能会尝试使这些字段变得特别(一个非常简单的扩展整数的 POJO)并为它们制作自定义转换器,当它们为空时返回所需的值。

        objectMapper.registerModule。您只需要为您创建的新特殊对象类型创建一个SimpleModule 和一些JsonSerializers。它们可能非常简单:

        public class SpecialOverriddenNumberSerializer
          extends JsonSerializer<SpecialOverriddenNumber>
        {
          private final static String ILLEGAL_VALUE_TEXT = "Some text";
        
          @Override
          public void serialize (
            SpecialOverriddenNumber value,
            JsonGenerator jgen,
            SerializerProvider provider )
            throws IOException, JsonProcessingException
          {
            // ILLEGAL_VALUE could be equivalent to Integer.MAX_VALUE or something
            // (possibly even null -- though I'm not sure if that would work well
            // with the NON_NULL serialization setting)
            if(value == SpecialOverriddenNumber.ILLEGAL_VALUE)
            {
              // jgen.writeNull();
              jgen.writeString(ILLEGAL_VALUE_TEXT);
            }
            else
            {
              jgen.writeNumber(value);
            }
          }
        }
        

        如果您还需要反序列化器,您可以简单地创建一个JsonDeserializer,如果它读取ILLEGAL_VALUE_TEXT,则返回null

        【讨论】:

        • 感谢您详细介绍此方法。我不认为将 pojo 转换为树结构是“序列化”它,这可能就是事情看起来很奇怪的原因。最终,我有一堆指向空数字的 json 指针,它们应该导致它们序列化为字符串值(这些指针的内联表示)。在运行时将这些指针与 pojo 分开处理也很方便,因此我仍然更喜欢删除它们并读取它们。
        • 我相信自定义 JsonSerializer 是处理此类事情的标准方式。如果您愿意,您仍然可以创建多个ObjectMappers。 1 用特殊字符串替换某些空值(使用JsonSerializers)并删除所有其他空值,1 删除所有空值,1 保留所有空值,等等......虽然根据我对你的情况的理解,这种特殊行为的标准(需要特殊处理一些空值并删除其他的)是创建一个JsonSerializer 来序列化整个 POJO。
        • ...是的,我明白你对JsonNode 的令人困惑的半序列化状态的意思。它不是您的反序列化 POJO,也不是 String(人们在使用术语序列化时通常是这个意思)或 byte[]。它是(正如@StaxMan 提到的)序列化的所见即所得的 JSON 结构位。
        猜你喜欢
        • 2015-10-05
        • 2012-03-17
        • 1970-01-01
        • 2017-04-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-04
        • 2018-02-28
        相关资源
        最近更新 更多