如何在JsonNode/ObjectNode中动态替换指定键的JSON节点值?
如何在动态结构的JsonNode中替换指定键的值(Java Jackson)
嘿,这个场景我之前做需求的时候刚好碰到过!确实,Jackson的JsonNode本身是不可变的,直接用put或replace方法根本行不通——尤其是当你不知道目标键的具体嵌套路径时,必须得遍历整个JSON结构来找目标键。
核心思路
我们需要递归遍历所有JSON节点,找到所有匹配目标键(这里是zipcode)的字段,然后替换它的值。注意要把不可变的JsonNode转换成可变的ObjectNode(Jackson的可变节点实现)才能修改内容,还要处理数组节点的情况(如果你的JSON里有数组的话)。
完整实现代码
下面是一个可直接复用的工具类方法,支持替换所有匹配目标键且旧值一致的节点:
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; public class JsonNodeModifier { private static final ObjectMapper objectMapper = new ObjectMapper(); // 替换指定键的旧值为新值,支持任意嵌套结构 public static JsonNode replaceValueByKey(JsonNode rootNode, String targetKey, String oldValue, String newValue) { // 先把根节点转为可变的ObjectNode(如果是对象的话) if (rootNode.isObject()) { ObjectNode objectNode = (ObjectNode) rootNode; // 遍历所有字段 objectNode.fields().forEachRemaining(entry -> { String key = entry.getKey(); JsonNode value = entry.getValue(); // 如果当前键是目标键,且值匹配旧值,直接替换 if (targetKey.equals(key) && oldValue.equals(value.asText())) { objectNode.put(key, newValue); } // 否则递归处理子节点 else if (value.isObject() || value.isArray()) { replaceValueByKey(value, targetKey, oldValue, newValue); } }); } // 处理数组节点,遍历每个元素递归处理 else if (rootNode.isArray()) { ArrayNode arrayNode = (ArrayNode) rootNode; for (int i = 0; i < arrayNode.size(); i++) { JsonNode element = arrayNode.get(i); replaceValueByKey(element, targetKey, oldValue, newValue); } } return rootNode; } // 测试用例 public static void main(String[] args) throws Exception { // 示例JSON一 String json1 = "{\"company\": \"xyz\", \"employee\": {\"name\": \"abc\", \"address\": {\"zipcode\": \"021566\"}}}"; // 示例JSON二 String json2 = "{\"name\": \"abc\", \"address\": {\"zipcode\": \"021566\"}}"; JsonNode modifiedJson1 = replaceValueByKey(objectMapper.readTree(json1), "zipcode", "021566", "566258"); JsonNode modifiedJson2 = replaceValueByKey(objectMapper.readTree(json2), "zipcode", "021566", "566258"); // 打印修改后的结果 System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(modifiedJson1)); System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(modifiedJson2)); } }
关键细节说明
- 不可变 vs 可变节点:Jackson的
JsonNode是只读的,必须转成ObjectNode或ArrayNode才能修改内容,这也是直接用JsonNode.put()无效的原因。 - 递归遍历:不管目标键嵌套多少层,递归都能找到它;如果JSON里有数组,也会遍历数组中的每个元素进行处理。
- 旧值匹配:代码里加了旧值判断,如果你想替换所有
zipcode键的值(不管原来是什么),直接去掉oldValue.equals(value.asText())这个条件就行。
测试结果
运行上面的main方法,两个示例JSON的zipcode值都会被成功替换成566258,输出的格式化JSON如下:
{ "company" : "xyz", "employee" : { "name" : "abc", "address" : { "zipcode" : "566258" } } }
{ "name" : "abc", "address" : { "zipcode" : "566258" } }
内容的提问来源于stack exchange,提问作者Sangeetharaj




