Java使用XPath从XML父节点获取子节点时返回非当前节点内容的问题
XPath查询当前节点子节点异常问题解决
问题场景
遍历XML中的Transaction节点列表,判断每个节点是否包含Reason子节点。使用getElementsByTagName能正常获取当前Transaction下的Reason,但用XPath表达式//Reason查询时,总是返回文档中第一个Reason节点的内容,而非当前父节点下的对应子节点。
输入XML
<root> <Transaction> <code> 123 </code> <Reason> test1 </Reason> </Transaction> <Transaction> <code> 456 </code> </Transaction> <Transaction> <code> 789 </code> <Reason> test2 </Reason> </Transaction> </root>
原Java代码
Document document = builder.parse(new FileInputStream(file)); XPath xPath = XPathFactory.newInstance().newXPath(); //Get all the transactions NodeList nodeList = (NodeList) xPath.evaluate("//Transaction", document, XPathConstants.NODESET); for (int temp = 0; temp < nodeList.getLength(); temp++) { Node node = nodeList.item(temp); Element element = (Element) node; if (node.getNodeType() == Node.ELEMENT_NODE) { XPath xPath2 = XPathFactory.newInstance().newXPath(); //This one always return first value Node child = (Node) xPath2.evaluate("//Reason", node, XPathConstants.NODE); if(child != null) { System.out.println(child.getTextContent()); } //This is working as expected if(element.getElementsByTagName("Reason").getLength() > 0) { System.out.println(element.getElementsByTagName("Reason").item(0).getTextContent()); } } }
问题原因
XPath表达式//Reason的语义是从文档根节点开始递归查找所有匹配的节点,即使传入了当前Transaction节点作为上下文,//依然会忽略上下文从根节点遍历,因此每次都返回整个文档中第一个Reason节点。
解决方案
1. 使用相对路径表达式
将//Reason改为.//Reason:
.表示当前上下文节点(即循环中的Transaction节点).//Reason会从当前节点开始递归查找所有子节点中的Reason元素
2. 优化XPath实例创建
不需要在循环内每次新建XPath实例,复用外部创建的xPath对象即可,减少不必要的资源开销。
修改后的代码示例
Document document = builder.parse(new FileInputStream(file)); XPath xPath = XPathFactory.newInstance().newXPath(); //Get all the transactions NodeList nodeList = (NodeList) xPath.evaluate("//Transaction", document, XPathConstants.NODESET); for (int temp = 0; temp < nodeList.getLength(); temp++) { Node node = nodeList.item(temp); Element element = (Element) node; if (node.getNodeType() == Node.ELEMENT_NODE) { // 使用相对路径从当前节点查找Reason,复用外部的xPath实例 Node child = (Node) xPath.evaluate(".//Reason", node, XPathConstants.NODE); if(child != null) { System.out.println(child.getTextContent()); } // 原有效代码保留 if(element.getElementsByTagName("Reason").getLength() > 0) { System.out.println(element.getElementsByTagName("Reason").item(0).getTextContent()); } } }
额外优化
如果Reason是Transaction的直接子节点(非嵌套在其他节点内),可以用更高效的./Reason表达式,避免递归查找:
Node child = (Node) xPath.evaluate("./Reason", node, XPathConstants.NODE);
内容的提问来源于stack exchange,提问作者Noman Ellahi
相关产品推荐
相关产品推荐

