简体   繁体   English

如何区分空值字段和杰克逊库中不存在的字段

[英]How to differentiate null value fields from absent fields in jackson library

We are consuming an API and the api is providing xml fields.我们正在使用 API,该 API 提供 xml 字段。 We have to convert xml to json for our consumers.我们必须为我们的消费者将 xml 转换为 json。 We have a requirement of showing just what we have got as XML and to display only those fields .我们需要以 XML 形式显示我们所获得的内容,并且只显示那些字段。

  1. If fields exists with value present it如果字段存在且存在值
  2. If fields does not exist don't present it如果字段不存在,则不显示它
  3. if field exists with null/no value display the field as it is .如果字段存在空值/无值,则按原样显示该字段。

What I have seen is general annotations我看到的是一般注释

@JsonInclude(NON_EMPTY) can be used to exclude values that are empty.I cannot use this because I still want to see the empty fields with null value in json @JsonInclude(NON_EMPTY)可用于排除空值。我不能使用它,因为我仍然想在 json 中看到具有空值的空字段

@JsonInclude(NON_ABSENT) can be used to exclude null values and values that are "absent".I cannot use this because I still want to see the empty fields and null fields in json. @JsonInclude(NON_ABSENT)可用于排除空值和“不存在”的值。我不能使用它,因为我仍然想在 json 中看到空字段和空字段。 Same with the JsonInclude (NON_NULL)JsonInclude (NON_NULL)

So my question is if I don't specify any of these properties can I achieve what I want ?所以我的问题是,如果我不指定任何这些属性,我可以实现我想要的吗? In other words if I don't specify any of these is jackson's behavior is to show all the fields that are have the null value on dynamic sense ?换句话说,如果我不指定其中任何一个,jackson 的行为是显示动态意义上具有空值的所有字段? My major concern is the dynamic response here .我主要关心的是这里的动态响应。 For each requests the fields may be present or not be present .对于每个请求,字段可能存在或不存在。 We have to show in the json what exactly we receive in XML我们必须在 json 中显示我们在 XML 中接收到的内容

If you want to differentiate null value fields from absent fields the most generic method will be using Map or JsonNode instead of POJO .如果您想区分null值字段和不存在的字段,最通用的方法将使用MapJsonNode而不是POJO POJO class has constant structure, Map or JsonNode have dynamic - contains only what you actually put there. POJO类具有恒定结构, MapJsonNode具有动态-仅包含您实际放置在那里的内容。 Let's create a simple app which reads XML payload from file and creates JSON response:让我们创建一个简单的应用程序,它从文件中读取XML负载并创建JSON响应:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;

import java.io.File;
import java.util.Map;

public class JsonApp {

    public static void main(String[] args) throws Exception {
        File xmlFile = new File("./resource/test.xml").getAbsoluteFile();

        XmlMapper xmlMapper = new XmlMapper();
        Map map = xmlMapper.readValue(xmlFile, Map.class);

        ObjectMapper jsonMapper = new ObjectMapper();
        String json = jsonMapper.writeValueAsString(map);

        System.out.println(json);
    }
}

Now take a look on some examples where we test what JSON will be generated for empty , null and absent nodes.现在看一些示例,其中我们测试将为empty节点、 null节点和不存在节点生成什么JSON

Test 0-0测试 0-0

Input XML :输入XML :

<Root>
    <a>A</a>
    <b>1</b>
    <c>
        <c1>Rick</c1>
        <c2>58</c2>
    </c>
</Root>

Result JSON is:结果JSON是:

{"a":"A","b":"1","c":{"c1":"Rick","c2":"58"}}

Test 0-1测试 0-1

Input XML :输入XML :

<Root>
    <a>A</a>
    <c>
        <c1>Rick</c1>
        <c2/>
    </c>
</Root>

Output JSON :输出JSON

{"a":"A","c":{"c1":"Rick","c2":null}}

Test 0-2测试 0-2

Input XML :输入XML :

<Root>
    <c/>
</Root>

Output JSON :输出JSON

{"c":null}

The biggest problem with this simple and fast solution is we lost type information for primitives.这种简单快速的解决方案的最大问题是我们丢失了原语的类型信息。 For example, if b is Integer we should return it in JSON as number primitive which does not have quotes: " chars around. To solve this problem we should use POJO model which allows us to find all required types. Let's create POJO model for our example:例如,如果bInteger我们应该在JSON中将它作为没有引号的数字原语返回: "字符。为了解决这个问题,我们应该使用POJO模型,它允许我们找到所有需要的类型。让我们为我们的创建POJO模型例子:

@JsonFilter("allowedFields")
class Root {
    private String a;
    private Integer b;
    private C c;

    // getters, setters
}

@JsonFilter("allowedFields")
class C {
    private String c1;
    private Integer c2;

    // getters, setters
}

We need to change our simple XML -> Map -> JSON algorithm to below one:我们需要将简单的XML -> Map -> JSON算法更改为以下算法:

  1. Read JSON as Map or JsonNode将 JSON 读取为MapJsonNode
  2. Find all field names查找所有字段名称
  3. Create FilterProvider with found names - notice that filter is registered with allowedFields name, the same which is used in @JsonFilter annotation.使用找到的名称创建FilterProvider - 请注意,过滤器使用allowedFields名称注册,与@JsonFilter注释中使用的名称相同。
  4. Convert Map to POJO for type coercion.Map转换为POJO以进行类型强制。
  5. Write POJO with filter用过滤器编写POJO

Simple app could look like this:简单的应用程序可能如下所示:

import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;

import java.io.File;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;

public class JsonApp {

    public static void main(String[] args) throws Exception {
        File xmlFile = new File("./resource/test.xml").getAbsoluteFile();

        NodesWalker walker = new NodesWalker();

        XmlMapper xmlMapper = new XmlMapper();
        JsonNode root = xmlMapper.readValue(xmlFile, JsonNode.class);
        Set<String> names = walker.findAllNames(root);

        SimpleFilterProvider filterProvider = new SimpleFilterProvider();
        filterProvider.addFilter("allowedFields", SimpleBeanPropertyFilter.filterOutAllExcept(names));

        ObjectMapper jsonMapper = new ObjectMapper();
        jsonMapper.setFilterProvider(filterProvider);

        Root rootConverted = jsonMapper.convertValue(root, Root.class);
        String json = jsonMapper.writeValueAsString(rootConverted);

        System.out.println(json);
    }
}

class NodesWalker {

    public Set<String> findAllNames(JsonNode node) {
        Set<String> names = new HashSet<>();

        LinkedList<JsonNode> nodes = new LinkedList<>();
        nodes.add(node);
        while (nodes.size() > 0) {
            JsonNode first = nodes.removeFirst();
            if (first.isObject()) {
                ObjectNode objectNode = (ObjectNode) first;
                objectNode.fields().forEachRemaining(e -> {
                    names.add(e.getKey());
                    JsonNode value = e.getValue();
                    if (value.isObject() || value.isArray()) {
                        nodes.add(value);
                    }
                });
            } else if (first.isArray()) {
                ArrayNode arrayNode = (ArrayNode) first;
                arrayNode.elements().forEachRemaining(e -> {
                    if (e.isObject() || e.isArray()) {
                        nodes.add(e);
                    }
                });
            }
        }

        return names;
    }
}

Test 1-0测试 1-0

Input XML :输入XML :

<Root>
    <a>A</a>
    <b>1</b>
    <c>
        <c1>Rick</c1>
        <c2>58</c2>
    </c>
</Root>

Output JSON :输出JSON

{"a":"A","b":1,"c":{"c1":"Rick","c2":58}}

Test 1-1测试 1-1

Input XML :输入XML :

<Root>
    <b>1</b>
    <c>
        <c2/>
    </c>
</Root>

Output JSON :输出JSON

{"b":1,"c":{"c2":null}}

Test 1-2测试 1-2

Input XML :输入XML :

<Root>
    <c/>
</Root>

Output JSON :输出JSON

{"c":null}

After all these tests we see that dynamic checking whether field is null , empty or absent is not an easy task.在所有这些测试之后,我们看到动态检查 field 是nullempty还是absent并不是一件容易的事。 Even so, above 2 solutions work for simple models you should test them for all responses you want to generate.即便如此,以上 2 个解决方案适用于简单模型,您应该针对您想要生成的所有响应测试它们。 When model is complex and contains many complex annotations such as: @JsonTypeInfo , @JsonSubTypes on Jackson side or @XmlElementWrapper , @XmlAnyElement on JAXB side it make this task very hard to implement.当模型很复杂并且包含许多复杂的注释时,例如: @JsonTypeInfo@JsonSubTypesJackson端或@XmlElementWrapper@XmlAnyElementJAXB端,它会使这个任务很难实现。

I think the best solution in your example is to use @JsonInclude(NON_NULL) which send to client all set fields on XML side.我认为您示例中的最佳解决方案是使用@JsonInclude(NON_NULL)XML端的所有设置字段发送给客户端。 null and absent should be treated on client side identically. nullabsent应该在客户端被同等对待。 Business logic should not rely on the fact field is set to null or absent in JSON payload.业务逻辑不应依赖于事实字段在JSON负载中设置为nullabsent

See also:也可以看看:

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用Jackson反序列化空类型JSON字段 - How to deserialize null type JSON fields with Jackson 如何防止 Map 中的空值和 bean 中的空字段通过 Jackson 序列化 - How to prevent null values inside a Map and null fields inside a bean from getting serialized through Jackson 杰克逊中的多态反序列化将特定字段的值设置为null - Polymorphic deserialization in jackson sets value null for specific fields 如何将Jackson ObjectMapper配置为使用自定义值序列化某些bean的某些字段(如果它们为null)? - How can I configure a Jackson ObjectMapper to serialize certain fields of certain beans with a custom value if they are null? Jackson JSON库:如何实例化包含抽象字段的类 - Jackson JSON library: how to instantiate a class that contains abstract fields 如何告诉 Jackson 在序列化过程中忽略带有 EMPTY 或 NULL 字段的 Object? - How to tell Jackson to ignore a Object with EMPTY or NULL fields during serialization? Jackson ObjectMapper:如何从序列化中省略(忽略)某些类型的字段? - Jackson ObjectMapper: How to omit (ignore) fields of certain type from serialization? 区分json中不存在字段和字段值作为null发送的时间 - Differentiate between when a field is absent in a json and when its value is sent as null Jackson:将一个值反序列化为两个 java 字段 - Jackson: Deserialize one value to two java fields Jackson 使用可选字段的默认值反序列化记录 - Jackson Deserialize Record with default value for optional fields
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM