如何使用Jackson从Json文件中移除文本节点

huangapple go评论79阅读模式
英文:

How to Remove a text node from a Json file using Jackson

问题

我正在尝试使用Jackson API从Json中移除一个文本节点,但是我遇到了以下错误:

com.fasterxml.jackson.databind.node.TextNode无法转换为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();
    }
}

我明白为什么会出现这个问题,因为我试图将基本类型转换为对象,如何从Json中删除一个基本节点,我需要在这里做什么。

英文:

I'm trying to remove a text node from Json using Jackson api, but I'm getting below error :

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

not sure where I went to wrong.

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

I got why I'm getting here bcz I'm trying to convert primitive to Object, how to Delete a primitive node from a Json, what do I need to do here.

答案1

得分: 2

问题并不在于基本类型与对象之间的区别,而是因为 ObjectNode 代表了一个 JSON 对象 - 即一个 {...} 的结构,而 TextNode 代表了 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"}}

所有的文本、布尔和数字字段都已被删除 - 只有 "details" 对象 {...} 没有被删除。如果测试输入包含一个数组 [...],该数组也将被保留。

请注意,此解决方案仅迭代 JSON 测试数据的顶层节点。它不会迭代 "details" 数组内部的子节点。如果您想深入访问 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 数组 [...]

更新 - 空容器

删除文本/布尔/数字节点的一个结果是,您可能会得到 JSON 中的空容器:{}[]

您可能需要一个或多个后续步骤来删除这些内容。

要检查的条件是:

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

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

我处理这种情况的方式如下:

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

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

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

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

这对于清理嵌套数组和对象是必要的:[ [ [...] ] ],其中第一次遍历只能清理最内层的数组,随后的遍历将删除结果中的空数组。

英文:

The problem here is not because of primitives vs. objects, but because an ObjectNode represents a JSON object - namely a {...} structure, whereas a TextNode represents a piece of JSON text &quot;foo&quot;. You can't convert between these different Java objects representing these different JSON structures.

To delete sections of a JSON structure, I would recommend using an iterator.

Assume we have some test data:

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

I will process this as a string in the following code example:

String jsonText = &quot;{\&quot;count\&quot;:123,\&quot;active\&quot;:true,\&quot;message\&quot;:\&quot;hello world\&quot;,\&quot;details\&quot;:{\&quot;firstName\&quot;:\&quot;John\&quot;,\&quot;lastName\&quot;:\&quot;Smith\&quot;}}&quot;;
System.out.println(jsonText);
        
ObjectMapper mapper = new ObjectMapper();
JsonNode json = mapper.readValue(jsonText, JsonNode.class);
        
Iterator&lt;JsonNode&gt; 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);

The output from this is as follows:

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

All the text, boolean, and number fields have been removed - only the "details" object {...} is not deleted. If the test input had contained an array [...], that would also have been preserved.

Note that this solution only iterates over the top level of nodes in the JSON test data. It does not iterate over the subnodes inside the "details" array. If you want to drill down into the hierarchy of your JSON, you would need a modified approach - for example by recursively calling a method:

private JsonNode removeUnwantedNodes(JsonNode json) {
    Iterator&lt;JsonNode&gt; 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;
}

In this example, the isContainerNode() method checks for JSON objects {...} and JSON arrays [...].

Update - Empty Containers

One consequence of deleting text/boolean/number nodes is that you can end up with empty containers in your JSON: {} and [].

You may need one or more subsequent passes to remove these.

The condition to check is:

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

This can be added to the approach shown above.

The way I would handle this is as follows:

(a) Create a deep copy of the JSON before iterating over it (for node removal). Make the copy using JsonNode prevJson = json.deepCopy();.

(b) Use a while loop to repeatedly iterate over the JSON, comparing the resulting JSON with the previous copy:

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

Once the removal process can no longer find anything that needs removing, you are done.

This is necessary to clean up things such as nested arrays and objects: [ [ [...] ] ], where a first pass will only be able to clean up the innermost array, and subsequent passes will remove the resulting empty arrays.

huangapple
  • 本文由 发表于 2020年8月25日 20:41:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/63579097.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定