当前位置: 首页 > 知识库问答 >
问题:

jackson库中空值字段与缺值字段的区分

支彭亮
2023-03-14
    null

我看到的是一般的注释

@jsoninclude(NON_EMPTY)可用于排除空值。我不能使用此方法,因为我仍然希望在json中看到具有空值的空字段

@jsoninclude(non_excent)可以用来排除空值和“不存在”的值。我不能使用这个,因为我仍然想看到JSON中的空字段和空字段。与JSONInclude(NON_NULL)相同

所以我的问题是,如果我不指定这些属性中的任何一个,我能实现我想要的吗?换句话说,如果我没有指定其中的任何一个,那么Jackson的行为是显示所有在动态意义上具有空值的字段吗?我主要关心的是这里的动态反应。对于每个请求,字段可能存在或不存在。我们必须在json中显示我们在XML中得到的确切信息

共有1个答案

班玉堂
2023-03-14

如果要区分null值字段和缺省字段,最通用的方法是使用mapJsonNode而不是POJOPOJO类具有常量结构,MapJsonNode具有动态结构-仅包含您实际放在那里的内容。让我们创建一个简单的应用程序,它从文件中读取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);
    }
}

现在来看看一些示例,在这些示例中,我们测试将为emptynull和缺省节点生成什么JSON

输入xml:

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

结果json为:

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

输入xml:

<Root>
    <a>A</a>
    <c>
        <c1>Rick</c1>
        <c2/>
    </c>
</Root>
{"a":"A","c":{"c1":"Rick","c2":null}}
<Root>
    <c/>
</Root>

输出JSON:

{"c":null}

这种简单而快速的解决方案最大的问题是丢失了原语的类型信息。例如,如果binteger我们应该在JSON中返回它,作为没有引号的number基元:字符。要解决这个问题,我们应该使用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
}

我们需要将简单的xml->map->json算法更改为以下一个:

  1. 将JSON读取为mapJsonNode
  2. 查找所有字段名
  3. 使用找到的名称创建filterprovider-请注意,筛选器使用AllowedFields名称注册,与@JSONFilter批注中使用的名称相同。
  4. map转换为POJO以获取类型强制。
  5. 使用筛选器编写POJO

简单的应用程序可能如下所示:

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

输入xml:

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

输出JSON:

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

输入xml:

<Root>
    <b>1</b>
    <c>
        <c2/>
    </c>
</Root>
{"b":1,"c":{"c2":null}}
<Root>
    <c/>
</Root>
{"c":null}

我认为在您的示例中最好的解决方案是使用@jsoninclude(NON_NULL),它将xml端的所有设置字段发送给客户端。null缺省应在客户端相同地处理。业务逻辑不应依赖于事实字段在JSON有效负载中被设置为null缺席

另见:

  • JSON中空与空的约定是什么?
  • 在JSON中表示null
 类似资料:
  • 是否有一个注释或其他方法告诉Jackson序列化一个字符串变量的值而不加引号。序列化程序应该只序列化一个字段的值,而不带引号。 而不是 以上是外部java脚本库需要数据的方式,因此如果没有方法,我将不得不在对象映射器将字符串转换为JSON之后手动更改字符串。

  • 问题内容: 考虑具有一个字符串和两个数组的JSON表示形式。例如, 在上述情况下,需要现场,但和被 有条件地 基于所述值所需的反序列化。换句话说,如果只需要有值和如果只要求有一个值。 当前,我在Jackson和Java中工作,并且能够通过创建以下代码来实现使字段成为强制性的: 但我不能只是附加其他到或因为它依赖的价值。 我怎么能有条件地要求和反序列化基础上的价值? 另外,我将要执行额外的检查,例如

  • 以下是有趣的: 它会生成如下错误: 只要我将其中一个空值更改为非空值,它就会工作。我想我明白了,因为在这个领域无法做出任何推断,但这确实看起来很奇怪。想法?

  • 大家好,我正在用@JSONProperty的自定义名称序列化嵌套对象。所以,这里是: 所以,在每一个测试中,我都看到非自定义的名称--“name”。我用@JsonProperty添加了getter,但没有成功。 我的测试类: 测试结果:预期:{“Payload”:{“CustomName”:“Name”}}实际:{“Payload”:{“Name”:“Name”}} org.opentest4j.

  • 我已经把这个贴在ES组上了,但是我没有得到回应,所以我把它贴在了so上。链接https://discuse . elastic . co/t/missing-field-value-for-nested-field-function-score-query-V7-10-2/291365 我已经尝试了很长时间,但嵌套字段值在计算分数时总是表示缺少字段值。 映射: 为了简单起见,我只保留了一个记录。记

  • 我在SQL Server 2012中有两个表 表1 表2 每个用户可以拥有1到8个权限。 我需要创建一个视图,该视图将为我提供具有8个权限条目的UserID,其中任何未使用的条目都包含null值,即条目的顺序无关紧要。 这让我很困惑,我甚至不知道从哪里开始?