如何使用Delphi 7从XML中删除名称空间

问题描述:

我正在使用下面的代码从XML删除名称空间属性,但我没有成功。我只想从节点删除命名空间Action__CompIntfc__CIName如何使用Delphi 7从XML中删除名称空间

<Action__CompIntfc__CIName xmlns="http://schemas.xmlsoap.org/soap/encoding/"> 

下面是我的代码

procedure TForm1.Button1Click(Sender: TObject); 
var 
    xmldoc : IXMLDOMDocument; 
    xmlString : WideString; 
    RecNodelist: IXMLDOMNodeList; 
    DataNode: IXMLDOMElement; 
    attr : IXMLDOMAttribute; 
begin 
    xmlString := '<?xml version="1.0"?>' 
    +'<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">' 
    +'<SOAP-ENV:Body>' 
     +'<Action__CompIntfc__CIName xmlns="http://schemas.xmlsoap.org/soap/encoding/">' 
     +'<test>1</test>' 
     +'</Action__CompIntfc__CIName>' 
     +'<Action__CompIntfc__CIName xmlns="http://schemas.xmlsoap.org/soap/encoding/">' 
     +'<test>15</test>' 
     +'</Action__CompIntfc__CIName>' 
    +'</SOAP-ENV:Body>' 
    +'</SOAP-ENV:Envelope>'; 
    try 
    XMLdoc := CoDOMDocument.Create; 
    xmldoc.loadXML(xmlString); 
    RecNodelist := XMLdoc.selectNodes('//SOAP-ENV:Envelope/SOAP-ENV:Body/Action__CompIntfc__CIName'); 
    DataNode := RecNodelist.NextNode as IXMLDOMElement; 
    while DataNode <> nil do 
    begin 
     showmessage(DataNode.xml); 
     attr := DataNode.getAttributeNode('xmlns'); 
     DataNode.removeAttributeNode(attr); 
     showmessage(DataNode.xml); 
     DataNode := RecNodelist.NextNode as IXMLDOMElement; 
    end; 
    except 
    on e: Exception do 
    begin 
    ShowMessage(e.Message); 
    end; 
    end; 
end; 

删除命名空间 “的xmlns =” 后http://schemas.xmlsoap.org/soap/encoding/ “从XML从下面节点

<Action__CompIntfc__CIName xmlns="http://schemas.xmlsoap.org/soap/encoding/"> 

我期待我的XML是

<?xml version="1.0"?> 
<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> 
    <SOAP-ENV:Body> 

    <Action__CompIntfc__CIName> 
     <test>1</test> 
    </Action__CompIntfc__CIName> 

    <Action__CompIntfc__CIName> 
     <test>15</test> 
    </Action__CompIntfc__CIName> 

    </SOAP-ENV:Body> 
</SOAP-ENV:Envelope> 
+0

那么你的问题是什么?你实际得到了什么结果? – MartynA

+0

您无法更改DOM树中某个节点的名称空间,因此您需要分别在不使用名称空间的情况下在不同名称空间中创建新节点。例如XSLT可以做到这一点,它看起来像你的Delphi代码使用支持XSLT的MSXML,所以告诉我们你是否可以使用XSLT解决方案。 –

+0

@MartinHonnen:“你不能改变节点的名字空间”确实如此。在我的回答中,我已经用新创建的代替了所讨论的节点,正如你所建议的那样,但是不使用XSLT。 – MartynA

以下适用于我。

正如你所看到的,它通过迭代你的RecNodeList来寻找名称正确的节点。当找到它时,它将创建一个具有相同tagNametext属性的新节点,复制其属性(xmlns除外),然后用新节点替换现有节点。

它还复制节点的第一级子节点及其属性。如果你想复制这些子节点的子节点(如果有的话),最好写一个递归函数来完成它,但这不会在你的q中使用Xml。

当然,显示的方法对Xml文档的结构很敏感,所以相当脆弱。我没有试图找出答案,但我想像评论中提出的XSLT解决方案可能同样脆弱。

procedure TForm1.RemoveNS; 
var 
    xmldoc : IXMLDOMDocument; 
    xmlString : WideString; 
    Target : String; 
    RecNodelist: IXMLDOMNodeList; 
    DataNode: IXMLDOMElement; 
    NextNode : IXMLDOMNode; 
    NewNode: IXMLDOMElement; 
    AttrNode : IXMLDOmNode; 
    ChildNode : IXMLDomElement; 
    Map : IXMLDOMNamedNodeMap; 
    i, 
    j : Integer; 
begin 

    // remove Namespace only from nodes 
    // <Action__CompIntfc__CIName xmlns="http://schemas.xmlsoap.org/soap/encoding/"> 

    xmlString := '<?xml version="1.0"?>'#13#10 
    +'<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">'#13#10 
    +'<SOAP-ENV:Body>'#13#10 
     +'<Action__CompIntfc__CIName xmlns="http://schemas.xmlsoap.org/soap/encoding/" anattr="hello">'#13#10 
     +'<test attr="123">1</test>'#13#10 
     +'</Action__CompIntfc__CIName>'#13#10 
     +'<Action__CompIntfc__CIName xmlns="http://schemas.xmlsoap.org/soap/encoding/">'#13#10 
     +'<test>15</test>'#13#10 
     +'</Action__CompIntfc__CIName>'#13#10 
    +'</SOAP-ENV:Body>'#13#10 
    +'</SOAP-ENV:Envelope>'#13#10; 

    Memo1.Lines.Text := xmlString; 
    Target := 'Action__CompIntfc__CIName'; 
    xmldoc := CoDOMDocument.Create; 

    try 
    xmldoc.loadXML(xmlString); 
    RecNodelist := xmldoc.selectNodes('//SOAP-ENV:Envelope/SOAP-ENV:Body/Action__CompIntfc__CIName'); 

    DataNode := RecNodelist.NextNode as IXMLDOMElement; 
    while DataNode <> nil do 
    begin 
     NextNode := DataNode.nextSibling; 
     if CompareText(DataNode.nodeName, Target) = 0 then begin 
     NewNode := XMLDoc.createElement(DataNode.tagName); 
     NewNode.text := DataNode.Text; 

     // copy the existing node's Attributes 
     Map := DataNode.attributes; 
     for i := 0 to Map.length - 1 do begin 
      AttrNode := Map.item[i]; 
      if CompareText(AttrNode.NodeName, 'xmlns') <> 0 then 
      NewNode.SetAttribute(AttrNode.NodeName, AttrNode.NodeValue); 
     end; 

     // Create (first level) child nodes matching the existing node's 
     // children and any attributes they have 
     for i := 0 to DataNode.childNodes.length - 1 do begin 
      ChildNode := XMLDoc.createElement(DataNode.childNodes.item[i].nodeName); 
      ChildNode.text := DataNode.childNodes.item[i].Text; 

      Map := DataNode.childNodes.item[i].attributes; 
      for j:= 0 to Map.length - 1 do begin 
      AttrNode := Map.item[j]; 
      ChildNode.SetAttribute(AttrNode.NodeName, AttrNode.NodeValue); 
      end; 

      NewNode.appendChild(ChildNode); 
     end; 
     DataNode.parentNode.replaceChild(NewNode, DataNode); 
     end; 
     DataNode := NextNode as IXmlDOMElement; 
    end; 

    Memo2.Lines.Text := XmlDoc.documentElement.xml; 
    except 
    on e: Exception do 
    begin 
    ShowMessage(e.Message); 
    end; 
    end; 
    xmldoc := Nil; // not strictly necessary because it will get finalized when this procedure exits, but anyway 
end; 
+0

您需要编写一个递归函数或过程来以相同的方式处理子节点,否?这就是XSLT的优点,您可以在名称空间中编写一个匹配元素的模板,并以相同的方式轻松处理内容。 –

+0

子节点缺少上面的代码 Action__CompIntfc__CIName>。 – DelphiLearner

+0

所以我得到输出为 1 Action__CompIntfc__CIName> .....你能否改变你的代码? – DelphiLearner

作为替代DOM编程,这里是一个XSLT 1.0样式表,它应该做的工作:

<?xml version="1.0" encoding="UTF-8" ?> 
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" 
    xmlns:se="http://schemas.xmlsoap.org/soap/encoding/" exclude-result-prefixes="se"> 

    <xsl:template match="@*|node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="se:*"> 
     <xsl:element name="{local-name()}"> 
      <xsl:apply-templates select="@* | node()"/> 
     </xsl:element> 
    </xsl:template> 

</xsl:transform> 

它把

<?xml version="1.0"?> 
<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> 
    <SOAP-ENV:Body> 
     <Action__CompIntfc__CIName xmlns="http://schemas.xmlsoap.org/soap/encoding/"> 
      <test>1</test> 
     </Action__CompIntfc__CIName> 
     <Action__CompIntfc__CIName xmlns="http://schemas.xmlsoap.org/soap/encoding/"> 
      <test>15</test> 
     </Action__CompIntfc__CIName> 
    </SOAP-ENV:Body> 
</SOAP-ENV:Envelope> 

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"> 
    <SOAP-ENV:Body> 
     <Action__CompIntfc__CIName> 
      <test>1</test> 
     </Action__CompIntfc__CIName> 
     <Action__CompIntfc__CIName> 
      <test>15</test> 
     </Action__CompIntfc__CIName> 
    </SOAP-ENV:Body> 
</SOAP-ENV:Envelope> 

,如所示。

至于使用它与MSXML,您可以与您的输入文档,你加载上面样式表和结果文件到其中的第二个文件,使用transformNodeToObjecthttps://msdn.microsoft.com/en-us/library/ms766561%28v=vs.85%29.aspx,一起在

var 
    xmldoc : IXMLDOMDocument; 
    sheet : IXMLDOMDocument; 
    result : IXMLDOMDocument; 

和创建它们:

xmldoc := CoDOMDocument.Create; 
sheet := CoDOMDocument.Create; 
result := CoDOMDocument.Create; 

负载xmlDoc中为这样做,装入上述XSLT代码(无论是从使用load方法或从与loadXML一个字符串,因为你的xmlDoc中做的文件),和然后致电

xmldoc.transformNodeToObject(sheet, result);