java 使用 xPath 修改 XML 文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12459712/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Modify XML file with xPath
提问by brimborium
I want to modify an existing XML file using xPath. If the node doesn't exist, it should be created (along with it's parents if neccessary). An example:
我想使用 xPath 修改现有的 XML 文件。如果该节点不存在,则应创建它(如果需要,还应创建它的父节点)。一个例子:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<param0>true</param0>
<param1>1.0</param1>
</configuration>
And here are a couple of xPaths I want to insert/modify:
这是我要插入/修改的几个 xPath:
/configuration/param1/text() -> 4.0
/configuration/param2/text() -> "asdf"
/configuration/test/param3/text() -> true
The XML file should look like this afterwards:
之后的 XML 文件应如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<param0>true</param0>
<param1>4.0</param1>
<param2>asdf</param2>
<test>
<param3>true</param3>
</test>
</configuration>
I tried this:
我试过这个:
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
try {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
Document doc = domFactory.newDocumentBuilder().parse(file.getAbsolutePath());
XPath xpath = XPathFactory.newInstance().newXPath();
String xPathStr = "/configuration/param1/text()";
Node node = ((NodeList) xpath.compile(xPathStr).evaluate(doc, XPathConstants.NODESET)).item(0);
System.out.printf("node value: %s\n", node.getNodeValue());
node.setNodeValue("4.0");
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.transform(new DOMSource(doc), new StreamResult(file));
} catch (Exception e) {
e.printStackTrace();
}
The node is changed in the file after running this code. Exactly what I wanted. But if I use one of the below paths, node
is null (and therefore a NullPointerException
is thrown):
运行此代码后,文件中的节点将发生更改。正是我想要的。但是,如果我使用以下路径之一,node
则为 null(因此NullPointerException
抛出 a):
/configuration/param2/text()
/configuration/test/param3/text()
How can I change this code so that the node (and non existing parent nodes as well) are created?
如何更改此代码以便创建节点(以及不存在的父节点)?
EDIT: Ok, to clarify: I have a set of parameters that I want to save to XML. During development, this set can change (some parameters get added, some get moved, some get removed). So I basically want to have a function to write the current set of parameters to an already existing file. It should override the parameters that already exist in the file, add new parameters and leave old parameters in there.
编辑:好的,澄清一下:我有一组要保存到 XML 的参数。在开发过程中,这个集合可以改变(一些参数被添加,一些被移动,一些被删除)。所以我基本上想要一个函数来将当前的参数集写入一个已经存在的文件中。它应该覆盖文件中已经存在的参数,添加新参数并将旧参数保留在那里。
The same for reading, I could just have the xPath or some other coordinates and get the value from the XML. If it doesn't exist, it returns the empty string.
对于阅读,我可以只拥有 xPath 或其他一些坐标并从 XML 获取值。如果不存在,则返回空字符串。
I don't have any constraints on how to implement it, xPath, DOM, SAX, XSLT... It should just be easy to use once the functionality is written (like BeniBela's solution).
我对如何实现它没有任何限制,xPath、DOM、SAX、XSLT……一旦编写了功能,它应该很容易使用(如 BeniBela 的解决方案)。
So if I have the following parameters to set:
因此,如果我要设置以下参数:
/configuration/param1/text() -> 4.0
/configuration/param2/text() -> "asdf"
/configuration/test/param3/text() -> true
the result should be the starting XML + those parameters. If they already exist at that xPath, they get replaced, otherwise they get inserted at that point.
结果应该是起始 XML + 那些参数。如果它们已存在于该 xPath 中,则它们将被替换,否则它们将在该点插入。
回答by Dimitre Novatchev
Here is a simple XSLT solution:
这是一个简单的 XSLT 解决方案:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="param1/text()">4.0</xsl:template>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
<param2>asdf</param2>
<test><param3>true</param3></test>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
当此转换应用于提供的 XML 文档时:
<configuration>
<param0>true</param0>
<param1>1.0</param1>
</configuration>
the wanted, correct result is produced:
产生了想要的、正确的结果:
<configuration>
<param0>true</param0>
<param1>4.0</param1>
<param2>asdf</param2>
<test><param3>true</param3></test>
</configuration>
Do Note:
请注意:
An XSLT transformation never "updates in-place". It always creates a new result tree. Therefore, if one wants to modify the same file, typically the result of the transformation is saved under another name, then the original file is deleted and the result is renamed to have the original name.
XSLT 转换永远不会“就地更新”。它总是创建一个新的结果树。因此,如果要修改同一个文件,通常将转换的结果保存在另一个名称下,然后删除原始文件并将结果重命名为原始名称。
回答by BeniBela
If you want a solution without dependencies, you can do it with just DOM and without XPath/XSLT.
如果您想要一个没有依赖项的解决方案,您可以只使用 DOM 而不使用 XPath/XSLT。
Node.getChildNodes|getNodeName / NodeList.* can be used to find the nodes, and Document.createElement|createTextNode, Node.appendChild to create new ones.
Node.getChildNodes|getNodeName / NodeList.* 可用于查找节点,Document.createElement|createTextNode, Node.appendChild 可用于创建新节点。
Then you can write your own, simple "XPath" interpreter, that creates missing nodes in the path like that:
然后,您可以编写自己的简单“XPath”解释器,在路径中创建缺失节点,如下所示:
public static void update(Document doc, String path, String def){
String p[] = path.split("/");
//search nodes or create them if they do not exist
Node n = doc;
for (int i=0;i < p.length;i++){
NodeList kids = n.getChildNodes();
Node nfound = null;
for (int j=0;j<kids.getLength();j++)
if (kids.item(j).getNodeName().equals(p[i])) {
nfound = kids.item(j);
break;
}
if (nfound == null) {
nfound = doc.createElement(p[i]);
n.appendChild(nfound);
n.appendChild(doc.createTextNode("\n")); //add whitespace, so the result looks nicer. Not really needed
}
n = nfound;
}
NodeList kids = n.getChildNodes();
for (int i=0;i<kids.getLength();i++)
if (kids.item(i).getNodeType() == Node.TEXT_NODE) {
//text node exists
kids.item(i).setNodeValue(def); //override
return;
}
n.appendChild(doc.createTextNode(def));
}
Then, if you only want to update text() nodes, you can use it as:
然后,如果您只想更新 text() 节点,则可以将其用作:
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
Document doc = domFactory.newDocumentBuilder().parse(file.getAbsolutePath());
update(doc, "configuration/param1", "4.0");
update(doc, "configuration/param2", "asdf");
update(doc, "configuration/test/param3", "true");
回答by Shenghai.Geng
I've created a small project for using XPATH to create/update XML: https://github.com/shenghai/xmodifierthe code to change your xml is like:
我创建了一个使用 XPATH 创建/更新 XML 的小项目:https: //github.com/shenghai/xmodifier更改 xml 的代码如下:
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(true);
DocumentBuilder documentBuilder = builderFactory.newDocumentBuilder();
Document document = documentBuilder.parse(xmlfile);
XModifier modifier = new XModifier(document);
modifier.addModify("/configuration/param1", "asdf");
modifier.addModify("/configuration/param2", "asdf");
modifier.addModify("/configuration/test/param3", "true");
modifier.modify();
回答by vtd-xml-author
I would point you to a new/novel way of doing what you described, by using VTD-XML... there are numerous reasons why VTD-XML is far better than all other solutions provided for this question... here are a few links ...
我会通过使用VTD-XML向您指出一种新的/新颖的方式来完成您所描述的事情……为什么 VTD-XML 比为此问题提供的所有其他解决方案要好得多的原因有很多……这里有一些链接...
- Simplify XML processing with vtd-xml
- Manipulate XML the Ximple Way
- Processing XML with Java – A Performance Benchmark
dfs
文件系统
import com.ximpleware.*;
import java.io.*;
public class modifyXML {
public static void main(String[] s) throws VTDException, IOException{
VTDGen vg = new VTDGen();
if (!vg.parseFile("input.xml", false))
return;
VTDNav vn = vg.getNav();
AutoPilot ap = new AutoPilot(vn);
ap.selectXPath("/configuration/param1/text()");
XMLModifier xm = new XMLModifier(vn);
// using XPath
int i=ap.evalXPath();
if(i!=-1){
xm.updateToken(i, "4.0");
}
String s1 ="<param2>asdf</param2>/n<test>/n<param3>true</param3>/n</test>";
xm.insertAfterElement(s1);
xm.output("output.xml");
}
}