bri*_*ium 4 java xml xslt xpath file
我想使用xPath修改现有的XML文件.如果该节点不存在,则应创建该节点(如果需要,则与其父节点一起创建).一个例子:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<param0>true</param0>
<param1>1.0</param1>
</configuration>
Run Code Online (Sandbox Code Playgroud)
这里有几个我要插入/修改的xPath:
/configuration/param1/text() -> 4.0
/configuration/param2/text() -> "asdf"
/configuration/test/param3/text() -> true
Run Code Online (Sandbox Code Playgroud)
之后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>
Run Code Online (Sandbox Code Playgroud)
我试过这个:
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();
}
Run Code Online (Sandbox Code Playgroud)
运行此代码后,文件中的节点已更改.正是我想要的.但是如果我使用以下路径之一,node则为null(因此NullPointerException抛出a):
/configuration/param2/text()
/configuration/test/param3/text()
Run Code Online (Sandbox Code Playgroud)
如何更改此代码以便创建节点(以及非现有父节点)?
编辑:好的,澄清:我有一组参数,我想保存到XML.在开发过程中,这个集合可以改变(一些参数被添加,一些参数被移动,一些被删除).所以我基本上希望有一个函数将当前参数集写入现有文件.它应该覆盖文件中已存在的参数,添加新参数并在其中保留旧参数.
读取相同,我可以使用xPath或其他一些坐标并从XML中获取值.如果它不存在,则返回空字符串.
我对如何实现它没有任何限制,xPath,DOM,SAX,XSLT ......一旦编写了功能(比如BeniBela的解决方案),它应该很容易使用.
因此,如果我要设置以下参数:
/configuration/param1/text() -> 4.0
/configuration/param2/text() -> "asdf"
/configuration/test/param3/text() -> true
Run Code Online (Sandbox Code Playgroud)
结果应该是起始XML +那些参数.如果它们已经存在于该xPath中,则它们将被替换,否则它们将在该点插入.
这是一个简单的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>
Run Code Online (Sandbox Code Playgroud)
在提供的XML文档上应用此转换时:
<configuration>
<param0>true</param0>
<param1>1.0</param1>
</configuration>
Run Code Online (Sandbox Code Playgroud)
产生了想要的正确结果:
<configuration>
<param0>true</param0>
<param1>4.0</param1>
<param2>asdf</param2>
<test><param3>true</param3></test>
</configuration>
Run Code Online (Sandbox Code Playgroud)
请注意:
XSLT转换永远不会"就地更新".它总是创建一个新的结果树.因此,如果想要修改同一文件,通常将转换结果保存在另一个名称下,则删除原始文件并将结果重命名为原始名称.
如果你想要一个没有依赖关系的解决方案,你可以只使用DOM而不使用XPath/XSLT.
Node.getChildNodes | getNodeName/NodeList.*可用于查找节点,而Document.createElement | createTextNode,Node.appendChild可用于创建新节点.
然后你可以编写自己的,简单的"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));
}
Run Code Online (Sandbox Code Playgroud)
然后,如果您只想更新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");
Run Code Online (Sandbox Code Playgroud)