Delphi向XPath结果添加子元素

3

在IDOMNode和IXMLNode之间有些困惑。我想要将一个文档中的子元素追加到一个使用XPath选择的节点中。

我尝试了以下方法:

尝试1:

我使用IDOMNodeSelect.selectNodes(expression);从XPath结果节点N:IDOMNode中获取节点。如果我使用以下代码将其转换回IXMLNode:

intfDocAccess : IXmlDocumentAccess;
doc: TXMLDocument;
...
if Supports( N.OwnerDocument, IXmlDocumentAccess, intfDocAccess) then
    doc := intfDocAccess.DocumentObject
  else
    doc := nil;
  NodeAsIXMLNode := TXmlNode.Create( N, nil, doc );

如果向一个节点添加子节点会抛出“访问冲突”的错误,可能是因为NodeAsIXMLNode不在原始文档中,而只是为了类型兼容性而创建的副本...

尝试直接向XPath结果节点添加子节点:

XMLNode := XmlDoc.CreateElement( 'tag', '' );
N.appendChild( XMLNode as IDOMNode );

它抛出“接口不支持”的错误。我有一种感觉,xpath结果IDOMNode节点也不是原始IXMLDocument的成员,只是一些结果副本。这只是猜测。
那么我该如何使用xpath选择一个节点,然后将子元素节点附加到它上面呢?这样我的原始IXMLDocument就会得到更新。
更新:遍历整个xml文档树,并将IXMLNode的DOMNode与XPath结果DOMNode进行比较也不起作用——XPath结果节点不包含在原始文档中。尝试了msxml、adom和omnixml实现/XE7/。
更新2:设法使用第一种方法,只需替换即可。
doc := nil;

使用

doc := _xpathdoc as TXMLDocument; // _xpathdoc : the IXMLDocument

在转换函数中。

XPath仅用于搜索,而不是用于操作文档。您无法将XPath的IDOMNode转换回原始的IXMLNode,并且selectNodes()不会报告在文档中找到DOM节点的位置。迭代文档以查找匹配节点会破坏使用XPath的目的,您最好一开始就手动迭代文档,而不使用XPath。 - Remy Lebeau
谢谢,也许可以,但为什么不使用XPath呢?它非常方便。当然,我可以以一种使XPath结果无效的方式操作文档 - 但我不会这样做。请参见更新2,了解当前有效的方法。 - kgz
请注意,doc := TXMLDocument(_xpathdoc); 被认为是一种“不安全”的类型转换,如果转换失败,则会返回 nil。更“安全”的转换方式是使用 as 运算符,这样如果转换失败就会引发异常:doc := _xpathdoc as TXMLDocument;。请参阅 Embarcadero 的 DocWiki 上的 Casting Interface References to Objects - Remy Lebeau
谢谢,根据您的建议已经更新了上面的代码。 - kgz
1个回答

0

我不确定这是否有帮助,但假设您有一个XML文档

<Content>
  <Clients>
    <Client>
      <ID value="88"/>
      <Forename value="John"/>
      <Surname value="Smith"/>
    </Client>
  </Clients>
</Content>

加载到一个TMemo和一个TEdit中,edPathQuery包含/Content/Clients。然后,以下代码将找到Clients节点并向其附加一个新的节点+值。

procedure TForm1.btnLoadClick(Sender: TObject);
var
  XmlDoc: IXMLDOMDocument;
  NodeList : IXmlDOMNodeList;
  Node,
  NewNode : IXmlDomNode;
  E : IXmlDomElement;
  TextNode : IXMLDOMText;
  Value : String;
  I : Integer;
begin
  Memo2.Lines.Clear;
  XmlDoc := CoDOMDocument.Create; //CreateOleObject('Microsoft.XMLDOM') as IXMLDOMDocument;
  XmlDoc.Async := False;
  XmlDoc.LoadXML(Memo1.Lines.Text);
  if xmlDoc.parseError.errorCode <> 0 then
    raise Exception.Create('XML Load error:' + xmlDoc.parseError.reason);

  NodeList := XmlDoc.documentElement.SelectNodes(edPathQuery.Text);

  if NodeList.length > 0 then begin
    E := XMLDoc.createElement('Added');
    //  E.nodeValue := 'Something'; Note the error this raises.  Using the
    //  TextNode as below avoids this
    NewNode := E as IXMLDomNode;
    TextNode := XMLDoc.createTextNode('Data');
    NewNode.appendChild(TextNode);
    NodeList.item[0].appendChild(NewNode);
  end;
  Memo2.Lines.Text := XMLDoc.documentElement.xml;
end;

这段代码使用了随 D7 一起提供的 MSXML.Pas 导入单元,并且是使用当前版本的 MSXML.DLL 生成的(C:\WINDOWS\SYSTEM\MSXML.DLL)。

个人而言,我从未发现 Delphi 的“抽象”TXmlDocument有所帮助——我发现使用 MSXML 的对象更容易。 见仁见智...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接