首页 > 解决方案 > 如何使用 Jackson 从 Json 文件中删除文本节点

问题描述

我正在尝试使用 Jackson api 从 Json 中删除文本节点,但出现以下错误:

com.fasterxml.jackson.databind.node.TextNode cannot be cast to com.fasterxml.jackson.databind.node.ObjectNode

不知道我哪里出错了。

    ObjectMapper mapper = new ObjectMapper();
    
    JsonNode json = mapper.readValue(jsonText, JsonNode.class);
    for (JsonNode node : json) {
                if (node.isNull() || node.isBoolean() || node.isNumber() || node.isTextual()) {
                        ((ObjectNode) node).removeAll();
    }
}

我明白了为什么我会来到这里 bcz 我正在尝试将原语转换为对象,如何从 Json 中删除原语节点,我需要在这里做什么。

标签: javajsonjackson

解决方案


这里的问题不是因为原语与对象,而是因为 anObjectNode代表一个 JSON 对象——即一个{...}结构,而 aTextNode代表一段 JSON 文本"foo"。您无法在代表这些不同 JSON 结构的这些不同 Java 对象之间进行转换。

要删除 JSON 结构的部分,我建议使用迭代器。

假设我们有一些测试数据:

{
    "count": 123,
    "active": true,
    "message": "hello world",
    "details": {
        "firstName": "John",
        "lastName": "Smith"
    }
}

我将在以下代码示例中将其作为字符串处理:

String jsonText = "{\"count\":123,\"active\":true,\"message\":\"hello world\",\"details\":{\"firstName\":\"John\",\"lastName\":\"Smith\"}}";
System.out.println(jsonText);
        
ObjectMapper mapper = new ObjectMapper();
JsonNode json = mapper.readValue(jsonText, JsonNode.class);
        
Iterator<JsonNode> it = json.iterator();
while (it.hasNext()) {
    JsonNode node = it.next();
    if (node.isNull() || node.isBoolean() || node.isNumber() || node.isTextual()) {
        it.remove(); 
    } 
}

String outputText = mapper.writeValueAsString(json);
System.out.println(outputText);

输出如下:

{"count":123,"active":true,"message":"hello world","details":{"firstName":"John","lastName":"Smith"}}
{"details":{"firstName":"John","lastName":"Smith"}}

所有文本、布尔值和数字字段都已被删除 - 只有“详细信息”对象{...}没有被删除。如果测试输入包含一个数组[...],那也将被保留。

请注意,此解决方案仅迭代 JSON 测试数据中的顶级节点。它不会遍历“详细信息”数组中的子节点。如果您想深入了解 JSON 的层次结构,则需要修改方法 - 例如通过递归调用方法:

private JsonNode removeUnwantedNodes(JsonNode json) {
    Iterator<JsonNode> it = json.iterator();
    while (it.hasNext()) {
        JsonNode node = it.next();
        if (node.isNull() || node.isBoolean() || node.isNumber() || node.isTextual()) {
            it.remove();
        } else if (node.isContainerNode()) {
            removeUnwantedNodes(node);
        }
    }
    return json;
}

在此示例中,该isContainerNode()方法检查 JSON 对象{...}和 JSON 数组[...]

更新 - 空容器

删除 text/boolean/number 节点的一个后果是您的 JSON:{}[].

您可能需要一个或多个后续通行证才能删除这些通行证。

检查的条件是:

if (node.isContainerNode() && node.isEmpty()) {
    it.remove();
}

这可以添加到上面显示的方法中。

我的处理方式如下:

(a) 在对其进行迭代之前创建 JSON 的深层副本(用于删除节点)。使用 制作副本JsonNode prevJson = json.deepCopy();

(b) 使用while循环重复迭代 JSON,将生成的 JSON 与之前的副本进行比较:

while (!json.equals(prevJson)) {
    prevJson = json.deepCopy();
    json = iterateForRemoval(json);
}

一旦删除过程不再找到需要删除的任何内容,您就完成了。

这对于清理诸如嵌套数组和对象之类的东西是必要的[ [ [...] ] ],其中第一次传递只能清理最里面的数组,随后的传递将删除生成的空数组。


推荐阅读