6

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

このコードを実行すると、ファイル内のノードが変更されます。まさに私が欲しかったもの。ただし、以下のパスのいずれかを使用すると、nodenull になります (したがって、 aNullPointerExceptionがスローされます)。

/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 に既に存在する場合は置き換えられ、そうでない場合はその時点で挿入されます。

4

4 に答える 4

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 変換は、「その場で更新」されることはありません。常に新しい結果ツリーが作成されます。したがって、同じファイルを変更したい場合は、通常、変換の結果が別の名前で保存され、元のファイルが削除され、結果の名前が元の名前に変更されます。

于 2012-09-17T13:15:50.267 に答える
6

依存関係のないソリューションが必要な場合は、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");
于 2012-09-17T16:10:31.533 に答える
3

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();
于 2014-11-28T05:37:35.330 に答える