如何通过JSONPath获取对应JSON节点行号(基于Jackson、JayWayPath)
解决方案
核心思路
核心思路是提前构建「JSON节点路径-行号」的映射表,再结合JSONPath查询返回的匹配路径做关联查找,完全可以基于你现有的Jackson+Jayway JsonPath技术栈实现,不需要引入额外依赖。
具体实现步骤
- 用Jackson流式API遍历全量JSON,记录每个节点的路径和对应行号
Jackson的JsonParser提供了getCurrentLocation().getLineNr()方法可以直接获取当前解析节点的行号,遍历过程中动态拼接当前节点的JSONPath格式路径,把<路径, 行号>的对应关系存入HashMap即可。 - 配置Jayway JsonPath返回匹配路径而非节点值
开启Jayway的Option.AS_PATH_LIST配置,执行JSONPath查询时会直接返回所有匹配节点的JSONPath字符串,不需要解析节点值。 - 路径匹配拿到行号
用Jayway返回的路径字符串去第一步构建的映射表中查找,即可得到对应节点的行号。
代码示例
依赖(已有无需重复引入)
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.2</version> </dependency> <dependency> <groupId>com.jayway.jsonpath</groupId> <artifactId>json-path</artifactId> <version>2.8.0</version> </dependency>
构建路径-行号映射表
Map<String, Integer> pathLineMap = new HashMap<>(); JsonFactory factory = new JsonFactory(); try (JsonParser parser = factory.createParser(new File("target.json"))) { Deque<String> pathStack = new ArrayDeque<>(); pathStack.push("$"); JsonToken token; while ((token = parser.nextToken()) != null) { String currentPath = String.join(".", pathStack); // 记录值节点行号,如需记录键名行号可调整到FIELD_NAME触发时记录 if (token.isScalarValue()) { pathLineMap.put(currentPath, parser.getCurrentLocation().getLineNr()); } // 处理对象键 if (token == JsonToken.FIELD_NAME) { pathStack.push(parser.getCurrentName()); } // 处理数组起始 else if (token == JsonToken.START_ARRAY) { pathStack.push(pathStack.pop() + "[0]"); } // 数组元素切换,索引自增 else if (token == JsonToken.VALUE_NUMBER_INT || token == JsonToken.VALUE_STRING || token == JsonToken.VALUE_TRUE || token == JsonToken.VALUE_FALSE || token == JsonToken.VALUE_NULL || token == JsonToken.END_OBJECT) { if (pathStack.peek().matches(".*\\[\\d+]$")) { String lastPart = pathStack.pop(); int idx = Integer.parseInt(lastPart.substring(lastPart.lastIndexOf('[')+1, lastPart.lastIndexOf(']'))); pathStack.push(lastPart.replaceAll("\\[\\d+]$", "[" + (idx+1) + "]")); } } // 退出对象/数组弹栈 else if (token == JsonToken.END_OBJECT || token == JsonToken.END_ARRAY) { if (pathStack.peek().matches(".*\\[\\d+]$")) { pathStack.push(pathStack.pop().replaceAll("\\[\\d+]$", "")); } pathStack.pop(); } } }
查询节点行号
// 配置Jayway返回路径而非值 Configuration config = Configuration.builder() .options(Option.AS_PATH_LIST) .build(); // 执行JSONPath查询 List<String> matchedPaths = JsonPath.using(config).parse(new File("target.json")).read("$[0].child_members[7].diamond.diamond_id"); // 匹配行号 for (String path : matchedPaths) { Integer lineNr = pathLineMap.get(path); System.out.println(path + " 对应行号:" + lineNr); }
注意事项
- 路径拼接逻辑可根据实际需求调整,保证和Jayway返回的路径格式完全一致即可正常匹配
- 支持任意复杂JSONPath查询,包括模糊匹配、条件过滤等返回多个结果的场景
内容的提问来源于stack exchange,提问作者Ankit Ostwal
相关产品推荐
相关产品推荐

