如何将子节点结构从一个 XML 文件复制到另一个 XML 文件(合并两个 XML 文件)?

How to copy a sub node structure from one XML file to another XML file (merge two XML files)?

我有以下两个 XML 文件:

文件1

<?xml version="1.0"?>
<main>
  <node1>
    <subnode1>
      <value1>101</value1>
      <value2>102</value2> 
      <value3>103</value3> 
    </subnode1>
    <subnode2>
      <value1>501</value1>
      <value2>502</value2> 
      <value3>503</value3> 
    </subnode2>
  </node1>
</main>

文件2

<?xml version="1.0"?>
<main>
  <node1>
    <subnode1>
      <value1>454</value1>
      <value2>471</value2> 
      <value3>498</value3> 
    </subnode1>
    <subnode2>
      <value1>723</value1>
      <value2>645</value2> 
      <value3>823</value3> 
    </subnode2>
  </node1>
</main>

在 Delphi 中,我想将 File2 的完整 <node1>...</node1> 结构添加到 File1 中,重命名为 <node2>...</node2>。所以结果应该是这样的:

<?xml version="1.0"?>
<main>
  <node1>
    <subnode1>
      <value1>101</value1>
      <value2>102</value2> 
      <value3>103</value3> 
    </subnode1>
    <subnode2>
      <value1>501</value1>
      <value2>502</value2> 
      <value3>503</value3> 
    </subnode2>
  </node1>
  <node2>
    <subnode1>
      <value1>454</value1>
      <value2>471</value2> 
      <value3>498</value3> 
    </subnode1>
    <subnode2>
      <value1>723</value1>
      <value2>645</value2> 
      <value3>823</value3> 
    </subnode2>
  </node2>
</main>

我已经问过如何提取问题中的 <node1>...</node1> (这确实是一个 XY 问题,对此感到抱歉)我可以通过将 XML 作为字符串进行操作来找到解决方案。

但我想可能会有更好的解决方案直接使用 XML 功能。那么我如何在 Delphi 10 中实现它?

一旦您将 XML 文件解析为 TXMLDocument/IXMLDocument objects, it is fairly trivial to clone/move IXMLNode objects from one document to the other (see IXMLNode.CloneNode(), or IXMLNodeList.Add() and IXMLNodeList.Remove()). Although you can't rename a node, you can create a new node with the desired name (see IXMLNode.AddChild()),然后 clone/move 旧节点的子节点位于新节点之下。

您不应该为这些任务使用 XML 字符串。而是操纵 DOM 树。因此,您将在 File2 中为 <node1> 获取 IXMLNode,在 File1 中为 <node2> 添加一个新的 IXMLNode,然后是 clone/move <subnode1><subnode2> 从 File2 中的 IXMLNode 到 File1 中的 IXMLNode


话虽如此,为什么您的元素按顺序命名? <node><subnode><value> 等就足够了。您可以根据需要在一个文档中包含多个 <node>、多个 <subnode>、多个 <value>。 XML 擅长使用重复的元素名称。您实际上不需要在元素名称中使用序号,例如:

<?xml version="1.0"?>
<main>
  <node>
    <subnode>
      <value>101</value>
      <value>102</value> 
      <value>103</value> 
    </subnode>
    <subnode>
      <value>501</value>
      <value>502</value> 
      <value>503</value> 
    </subnode>
  </node>
  <node>
    <subnode>
      <value>454</value>
      <value>471</value> 
      <value>498</value> 
    </subnode>
    <subnode>
      <value>723</value>
      <value>645</value> 
      <value>823</value> 
    </subnode>
  </node>
</main>

如果你真的需要用数字来区分,你可以使用属性来代替,例如:

<?xml version="1.0"?>
<main>
  <node id="1">
    <subnode id="1">
      <value id="1">101</value>
      <value id="2">102</value> 
      <value id="3">103</value> 
    </subnode>
    <subnode id="2">
      <value id="1">501</value>
      <value id="2">502</value> 
      <value id="3">503</value> 
    </subnode>
  </node>
  <node id="2">
    <subnode id="1">
      <value id="1">454</value>
      <value id="2">471</value> 
      <value id="3">498</value> 
    </subnode>
    <subnode id="2">
      <value id="1">723</value>
      <value id="2">645</value> 
      <value id="3">823</value> 
    </subnode>
  </node>
</main>

您可以使用 IXMLNode.CloneNode().

这是一个完整的例子:

uses XMLIntf, XMLDoc, xmldom;

procedure Test;
var
    XML1, XML2: DOMString;
    Doc1, Doc2: IXMLDocument;
    NewNode, Node1: IXMLNode;
    i: integer;
begin
    XML1 := '<?xml version="1.0"?><main><node1><subnode>1</subnode></node1></main>';
    Doc1 := LoadXMLData(XML1);
    NewNode := Doc1.DocumentElement.AddChild('node2');

    XML2 := '<?xml version="1.0"?><main><node1><subnode>2</subnode></node1></main>';
    Doc2 := LoadXMLData(XML2);
    Node1 := Doc2.DocumentElement.ChildNodes['node1'];

    // Clone nodes from Doc2 to Doc1
    for i := 0 to Node1.ChildNodes.Count - 1 do
        NewNode.ChildNodes.Add(Node1.ChildNodes[i].CloneNode(true));

    Doc1.SaveToFile('merged.xml');
end;