我想使用xPath修改现有的XML文件。 如果该节点不存在,则应创建该节点(如果需要,则与其父节点一起创建)。 一个例子:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <param0>true</param0>
  <param1>1.0</param1>
</configuration>

这里有几个我要插入/修改的xPath:

/configuration/param1/text()         -> 4.0
/configuration/param2/text()         -> "asdf"
/configuration/test/param3/text()    -> true

之后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>

我试过这个:

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();
}

运行此代码后,文件中的节点已更改。 正是我想要的。 但是,如果我使用以下路径之一, node为null(因此抛出NullPointerException ):

/configuration/param2/text()
/configuration/test/param3/text()

如何更改此代码以便创建节点(以及非现有父节点)?

编辑 :好的,澄清:我有一组参数,我想保存到XML。 在开发过程中,这个集合可以改变(一些参数被添加,一些参数被移动,一些被删除)。 所以我基本上希望有一个函数将当前参数集写入现有文件。 它应该覆盖文件中已存在的参数,添加新参数并在其中保留旧参数。

读取相同,我可以使用xPath或其他一些坐标并从XML中获取值。 如果它不存在,则返回空字符串。

我对如何实现它没有任何限制,xPath,DOM,SAX,XSLT ......一旦编写了功能(比如BeniBela的解决方案),它应该很容易使用。

因此,如果我要设置以下参数:

/configuration/param1/text()         -> 4.0
/configuration/param2/text()         -> "asdf"
/configuration/test/param3/text()    -> true

结果应该是起始XML +那些参数。 如果它们已经存在于该xPath中,则它们将被替换,否则它们将在该点插入。

#1楼 票数:6

这是一个简单的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>

在提供的XML文档上应用此转换时:

<configuration>
    <param0>true</param0>
    <param1>1.0</param1>
</configuration>

产生了想要的正确结果:

<configuration>
   <param0>true</param0>
   <param1>4.0</param1>
   <param2>asdf</param2>
   <test><param3>true</param3></test>
</configuration>

请注意

XSLT转换永远不会“就地更新”。 它总是创建一个新的结果树。 因此,如果想要修改同一文件,通常将转换结果保存在另一个名称下,则删除原始文件并将结果重命名为原始名称。

#2楼 票数:5 已采纳

如果你想要一个没有依赖关系的解决方案,你可以只使用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));    
}

然后,如果您只想更新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");

#3楼 票数:2

我创建了一个使用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();

#4楼 票数:1

我会指出一种新的/新颖的方式来做你所描述的,使用VTD-XML ...有很多原因可以解释为什么VTD-XML比为这个问题提供的所有其他解决方案要好得多......这里有一些链接......

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");
            }
        }

  ask by brimborium translate from so

未解决问题?本站智能推荐:

3回复

XPATH:选择xml文件的子集

就我而言,我有: 我怎么才能回来: 如果我使用/booklist/book[@id=1] ,我只能得到 但我还需要文档元素。 谢谢
2回复

从XPath表达式填充XML模板文件?

从XPath表达式的映射填充(或生成)XML模板文件的最佳方法是什么? 要求是我们需要从模板开始(因为它可能包含XPath表达式中未捕获的信息)。 例如,起始模板可能是: 然后提供给我们,类似于: 然后,我们应该生成: 我是用Java实现的,尽管如果可能的话,
3回复

如何从一组XPath表达式生成XML文件?

我希望能够在给定一组XPath映射的情况下生成完整的XML文件。 输入可以在两个映射中指定:(1)一个列出XPath表达式和值; (2)定义适当名称空间的另一个。 对于名称空间: 另请注意,我也必须处理XPath 属性表达式。 例如:我还应该能够处理属性,例如:
2回复

xml的xpath正则表达式

我想使用xpath正则表达式读取xml。 标签可以是<PaymentCard>或<ns2:PaymentCard> 。 我需要找到一个公共的正则表达式来读取两个标签。 目前,我只能使用以下代码读取<PaymentCard>标签:
1回复

从XPATH创建XML

我是使用xml和XSL的新手,可以使用旧版平台... 我正在寻找从XPATH创建XML的解决方案。 碰巧看到这篇文章如何从一组XPath表达式生成XML文件? 这对我很有帮助。 与“注释”部分中讨论的请求类似,我试图将整个XSLT作为字符串传递,并使用Saxon接收结果作为字符串。
2回复

获取XML标签的XPath

如果我有一个如下的XML文档: 例如,如何获取<test1>的XPath? 因此输出应类似于: foo/foo2[2]/another1/test1 我猜代码看起来像这样: 假设String XPathVar = getXPath(document, "<
1回复

使用XSLT为所有节点生成XML的xpath

我试图找到一种将XML标记转换为它们各自的唯一地址(如xPath)的方法。 我发现一个XSLT,其中处理XML,并且仅为不具有子元素和属性的节点创建唯一地址。 [link]: 从XML节点java生成/获取xpath XSLT: 当传递给这个 产生 所以我需要以
1回复

通过使用Java和Xpath获取xml的所有属性

我有以下xml: 我想通过使用Java代码和Xpath阅读以上xml,检索子节点的id="2019-05-15-04.52.05.641880A01" and id="2019-05-15-04.52.05.901880A02" (即id="2019-05-15-04.52.05.641