如何通过XPATH更新需要不同Java值的多个节点?

时间:2013-06-24 18:58:17

标签: java xml xpath

当需要处理具有不同名称的字段时,如何通过一个XPATH语句更新多个节点?

2 个答案:

答案 0 :(得分:2)

我不确定你在发布这个问题时的目标是什么,但我看到你自己的答案有几个问题。

  • XPath是一种将谓词应用于DOM树的工具。使用XPath选择比您需要的更大的节点集合是没有意义的,然后将您自己的谓词应用于它返回的节点列表。
  • 您的XPath表达式包含多个正交术语。评估效率较低,并且在添加其他条款时很快就会变得无法管理。
  • 您正在使用的表达式 - 检索文本节点然后导航到父元素 - 并没有多大意义。我想你只想选择已有文本的节点,但是如果你正在进行全局搜索并替换我不确定这是一个合理的假设。如果我要继承你的代码,我当然希望看到证明这两种行为的测试,作为你想要这种行为的指示。
  • 众所周知,if / else语句的长链难以理解。它们也很难测试,因为你必须制作一个测试套件来执行代码中所有可能的路径。
  • if语句的正文是重复的代码,违反了DRY主体。鉴于您需要从所选节点向上导航,这可能会导致错误 - 或者复制并粘贴代码,如果您更改了指定表达式的方式,则必须在多个位置进行更改。

在我看来,提取一个选择节点并一次对单个表达式执行更改的方法要好得多。从您的代码开始,让我的IDE修复一些语法错误,我最终得到了这个(但是,我还没有编写测试用例):

public static void main(String[] args) throws Exception  
{  
     DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();  
     DocumentBuilder builder = factory.newDocumentBuilder();  
     Document doc = builder.parse(new File("path/to/file.xml"));  

     changeNodeText(doc, "/PersonList/Person/Age", "42");
     changeNodeText(doc, "/PersonList/Person/Name", "Batman");
}  


public static void changeNodeText(Node context, String xpath, String value)
throws XPathExpressionException
{
    XPathFactory xFactory = XPathFactory.newInstance();
    XPath xPath = xFactory.newXPath();
    XPathExpression expression = xPath.compile(xpath);
    NodeList nodes = (NodeList)expression.evaluate(context, XPathConstants.NODESET);
    for (int i = 0; i < nodes.getLength(); i++)
    {
        Node node = nodes.item(i);
        node.setTextContent(value);
    }
}

一些关键功能(除了我上面提到的XPath特定注释):

  • 函数名称表明您正在更改节点的文本。
  • 您可以轻松查看正在更新的路径;你不必通过代码来弄清楚它在做什么。
  • 您可以使用两个测试用例(正面和负面)进行测试,并推断它可以在任何地方使用。
  • 它使用setTextContent()方法,IMO的规范比setNodeValue()更好(除非您传递文本节点,否则会失败)。
  • 如果您必须添加命名空间映射,则可以在一个位置执行此操作。如果您决定只想更改已有文本节点的元素,则同上。

答案 1 :(得分:0)

给出这样的架构:

<?xml version="1.0" encoding="UTF-8"?>  
<PersonList>  
    <Person>  
          <Name>Adam West </Name>  
          <Age> 72 </Age>
    </Person>  
</PersonList>  

以下代码会将名称字段更新为Batman,将年龄字段更新为42

import javax.xml.parsers.DocumentBuilder;  
import javax.xml.parsers.DocumentBuilderFactory;  

import javax.xml.xpath.XPath; 
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
    public static void main(String[] args) throws Exception  
    {  
         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();  
         DocumentBuilder builder = factory.newDocumentBuilder();  
         Document doc = builder.parse(new File("path/to/file.xml"));  

         XPathFactory xFactory = XPathFactory.newInstance();  
         XPath xPath = xFactory.newXPath();  
         XPathExpression expression = xPath.compile("PersonList/Person/Age/text() | PersonList/Person/Name/text()");  

         NodeList nodes = (NodeList) expression.evaluate(doc,XPathConstants.NODESET);  
        for(int i = 0; i <nodes.getLength() i++)  
        {  
               Node node = nodes.item(i);   
               if(node.getPArentNode().getNodeName().equals("Name")  
               {  
                    node.setNodeValue("Batman");  
               }   else  
               {  
                   node.setNodeValue("42"); // you may need more else/if blocks / switch
               }  
         }  
    }