当需要处理不同名称的字段时,如何通过一个 XPATH 语句更新多个节点?
问问题
985 次
2 回答
2
I'm not sure what your goals were in posting this question, however I see several issues with your own answer.
- XPath is a tool for applying predicates to DOM trees. It makes no sense to use XPath to select a larger set of nodes than you need, and then apply your own predicates to the list of nodes that it returns.
- Your XPath expression contains multiple orthogonal terms. This is less efficient to evaluate, and will quickly become unmanageable as you add additional terms.
- The expressions that you're using -- retrieving the text nodes and then navigating to the parent Element -- doesn't make a lot of sense. I suppose that you're looking to only select nodes that already have text, but if you're doing a global search and replace I'm not sure that's a reasonable assumption. If I were to inherit your code I'd certainly want to see tests that demonstrate both behaviors, as an indication that you intended that behavior.
- Long chains of if/else statements are notoriously difficult to comprehend. They're also difficult to test, as you have to craft a test suite that executes all possible paths through the code.
- The body of your
if
statements is repeated code, violating the DRY principal. Given your need to navigate upward from the selected nodes, this opens up a possibility for errors -- or copy-and-paste code that has to be changed in multiple places if you ever change the way you specify your expressions.
In my opinion, it's far better to extract a method that selects nodes and performs changes for a single expression at a time. Starting from your code, and letting my IDE fix some of the syntax errors, I ended up with this (however, I haven't written testcases):
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);
}
}
Some of the key features (aside from the XPath-specific comments I made above):
- The function name makes it clear that you're changing the text of a node.
- You can easily see the paths that you're updating; you don't have to walk through code to figure out what it's doing.
- You can test this with two testcases (positive and negative) and infer that it will work anywhere.
- It uses the
setTextContent()
method, which IMO has a better specification thansetNodeValue()
(which will fail unless you pass text nodes). - If you ever have to add a namespace mapping, you can do it in one place. Ditto if you decide that you only want to change elements that already have text nodes.
于 2013-06-25T00:34:10.693 回答
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
}
}
}
于 2013-06-24T18:58:17.040 回答