如何从 XElements 列表中删除不需要的节点?

Posted

技术标签:

【中文标题】如何从 XElements 列表中删除不需要的节点?【英文标题】:How can I remove an unwanted node from a list of XElements? 【发布时间】:2021-10-06 05:37:07 【问题描述】:

我需要使用 C# 来处理来自服务的响应,然后再将其交给调用者。该服务接受一系列 XML 格式的请求,格式化消息并将其发送到服务。响应类似于:

<SvcRes>
    <SvcVer>1.0</SvcVer>
    <MsgUUID>12345678-1234-1234-1234-123456789012</MsgUUID>
    <Svc>
        <SvcParms>
            <ApplID>App</ApplID>
            <SvcID>AppSrch</SvcID>
            <SvcVer>1.0</SvcVer>
        </SvcParms>
        <MsgData>
            <AppResData>
                <Key>xxxxxxxx</Key>
                <Rslt>xxxxxxxx</Rslt>
                <ErrCde>0</ErrCde>
                <ApplMsgLst>
                    <ApplMsg>
                        <ApplMsgApplId>D6</ApplMsgApplId>
                        <ApplMsgNbr>0</ApplMsgNbr>
                        <ApplMsgTxt>INQUIRY COMPLETE     09:23:53</ApplMsgTxt>
                        <ApplMsgErrInd>N</ApplMsgErrInd>
                    </ApplMsg>
                </ApplMsgLst>
            </AppResData>
        </MsgData>
        <ErrCde>0</ErrCde>
        <ErrMsg/>
    </Svc>
    <Svc>
        <SvcParms>
            <ApplID>DP</ApplID>
            <SvcID>DPKywrd</SvcID>
            <SvcVer>1.0</SvcVer>
            <RqstUUID>12345678-1234-1234-1234-123456789012</RqstUUID>
        </SvcParms>
        <MsgData>
            <AppResData>
                <Key>xxxxxxxx</Key>
                <Rslt>xxxxxxxx</Rslt>
                <ErrCde>0</ErrCde>
                <ApplMsgLst>
                    <ApplMsg>
                        <ApplMsgApplId>D6</ApplMsgApplId>
                        <ApplMsgNbr>0</ApplMsgNbr>
                        <ApplMsgTxt>INQUIRY COMPLETE     09:23:53</ApplMsgTxt>
                        <ApplMsgErrInd>N</ApplMsgErrInd>
                    </ApplMsg>
                </ApplMsgLst>
            </AppResData>
        </MsgData>
        <ErrCde>0</ErrCde>
        <ErrMsg/>
      <Svc>
    <ErrCde>0</ErrCde>
    <ErrMsg>Success</ErrMsg>
</SvcRes>

我需要提取 AppResData 节点,然后从每个节点中提取 ApplMsgLst 节点,然后再将结果发送回调用方。生成的 XML 应该是这样的:

<AppResData>
    <Key>xxxxxxxx</Key>
    <Rslt>xxxxxxxx</Rslt>
    <ErrCde>0</ErrCde>
</AppResData>
<AppResData>
    <Key>xxxxxxxx</Key>
    <Rslt>xxxxxxxx</Rslt>
    <ErrCde>0</ErrCde>
</AppResData>

我可以使用以下 2 行代码获取 XElement 节点列表:

var xml = XElement.Parse(xmlResponse);
var msgData = xml.DescendantsAndSelf("MsgData");
然后我可以执行 string.Concat(msgData.Nodes()) 以获取最终字符串以返回给调用者 - 但此时我不知道如何删除内部 ApplMsgLst 节点。我尝试将其转换回字符串并重新解析,但它当然会抱怨多个根元素。我已经尝试了所有我能想到的 Remove 组合,但它们总是删除太多(全部)或什么都不删除..

还有其他方法可以做到这一点吗?没有文件,这是 SOAP Web 服务中的所有字符串数据。

【问题讨论】:

这个任务最好使用 XSLT。我希望您想要的输出格式正确,包含根节点。 【参考方案1】:

这是一个基于 XSLT 的解决方案。

参数:

输入 XML 作为字符串数据类型。 XSLT 作为文件。 将 XML 输出为字符串数据类型。

XSLT

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes" omit-xml-declaration="yes"/>

    <xsl:template match="/SvcRes">
        <root>
            <xsl:for-each select="Svc/MsgData/AppResData">
                <xsl:copy>
                    <xsl:copy-of select="Key"/>
                    <xsl:copy-of select="Rslt"/>
                    <xsl:copy-of select="ErrCde"/>
                </xsl:copy>
            </xsl:for-each>
        </root>
    </xsl:template>
</xsl:stylesheet>

c#

void Main()

    const string XSLTFILE = @"e:\Temp\DaveN59.xslt";
    string inputXML = @"<SvcRes>
        ...
    </SvcRes>";

    try
    
        StringReader rdr = new StringReader(inputXML);
        XPathDocument XPathDoc = new XPathDocument(rdr);

        XslCompiledTransform xslt = new XslCompiledTransform();
        xslt.Load(XSLTFILE);

        StringWriter sw = new StringWriter();
        using (XmlWriter xwo = XmlWriter.Create(sw, xslt.OutputSettings))
        
            xslt.Transform(XPathDoc, xwo);
        
        
        Console.WriteLine(sw.ToString());
    
    catch (Exception ex)
    
        Console.WriteLine(ex.Message);
    

输出 XML

<root>
  <AppResData>
    <Key>xxxxxxxx</Key>
    <Rslt>xxxxxxxx</Rslt>
    <ErrCde>0</ErrCde>
  </AppResData>
  <AppResData>
    <Key>xxxxxxxx</Key>
    <Rslt>xxxxxxxx</Rslt>
    <ErrCde>0</ErrCde>
  </AppResData>
</root>

【讨论】:

所以基本上只是将好的东西复制到一个新的字符串/XML结构中,然后将其转换为一个新的输出字符串。我不得不考虑一下,但由于我已经从更大的响应消息中提取“输入”XML(我的帖子中没有提到一个细节),所以这种方法应该可以很容易地同时进行,而不是运行 2 个不同的转换。我喜欢,我试试看! 这非常适合我的需要,谢谢!我确实添加了预编译 xslt 文件的初始步骤,而不是为每次迭代甚至每个实例加载文件。我们可以轻松地在 XML 输入中拥有数百个条目,以及数十个(如果不是数百个)同时调用。除此之外,这完全符合要求【参考方案2】:

您可以尝试使用 Cinchoo ETL - 一个可以轻松完成此转换的开源库

StringBuilder xml = new StringBuilder();

using (var r = ChoXmlReader.LoadText("*** YOUR XML CONTENTS ***")
    .WithXPath("//AppResData")
    .WithField("Key")
    .WithField("Rslt")
    .WithField("ErrCde")
    )

    using (var w = new ChoXmlWriter(xml)
        )
    
        w.Write(r);
    

Console.WriteLine(xml.ToString());

输出:

<Root>
  <AppResData>
    <Key>xxxxxxxx</Key>
    <Rslt>xxxxxxxx</Rslt>
    <ErrCde>0</ErrCde>
  </AppResData>
  <AppResData>
    <Key>xxxxxxxx</Key>
    <Rslt>xxxxxxxx</Rslt>
    <ErrCde>0</ErrCde>
  </AppResData>
</Root>

【讨论】:

虽然这个库听起来很有趣也很有用,但在投入生产之前我没有时间证明它,所以我采用了 Vitzhak 的更“裸机”方法

以上是关于如何从 XElements 列表中删除不需要的节点?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 C# 从 XML 节点列表中删除节点

如何从xml中删除列表父节点?

想要从 XElements 中提取标签并在 ASP.NET Webform 上显示为网格

如何从drupal 6中的视图中删除节点

从链接列表中删除节点无法正常工作

Linq-to-XML XElement.Remove() 留下不需要的空白