基于子节点对整个XDocument进行排序

5
我有以下格式的XML:

<?xml version="1.0" encoding="utf-8"?>
<contactGrp name="People">
  <contactGrp name="Developers">
    <customer name="Mike" ></customer>
    <customer name="Brad" ></customer>
    <customer name="Smith" ></customer>
  </contactGrp>
  <contactGrp name="QA">
    <customer name="John" ></customer>
    <customer name="abi" ></customer>
  </contactGrp>
</contactGrp>

我想根据客户的姓名对客户列表进行排序,并以以下格式返回文档:
<?xml version="1.0" encoding="utf-8"?>
<contactGrp name="People">
  <contactGrp name="Developers">
    <customer name="Brad" ></customer>
    <customer name="Mike" ></customer>
    <customer name="Smith" ></customer>
  </contactGrp>
  <contactGrp name="QA">
    <customer name="abi" ></customer>
    <customer name="John" ></customer>
  </contactGrp>
</contactGrp>

我正在使用C#,目前使用的是XmlDocument。
谢谢。

我很困惑,因为 xpath 标签... 如果您想选择一个节点集,则XPath可能是正确的技术。 如果您想正确排序节点集,则需要XPath引擎主机语言。 但是,如果您想转换XML树,则标准资源是XSLT。 - user357812
我知道,似乎无法解决我的问题。 - vondip
好问题,+1。请看我的答案,其中包含一个完整、简短且易于理解的 XSLT 解决方案,适用于任何层次嵌套的 contactGrp 元素。 :) - Dimitre Novatchev
3个回答

4
你可以像这样做:

您可以采取以下方法

var doc = XDocument.Load(/* ... */);

foreach (var g in doc.Descendants("contactGrp"))
{
    var customers = g.Elements("customer").ToList();
    customers.Remove();
    g.Add(customers.OrderBy(c => c.Attribute("name").Value));
}

2

如果你想要一个样式表并用它来转换文档,则:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
    <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/contactGrp">
    <contactGrp name="Developers">
      <xsl:apply-templates select="contactGrp"/>
    </contactGrp>
  </xsl:template>

  <xsl:template match="contactGrp/contactGrp">
    <contactGrp>
      <xsl:attribute name="name">
        <xsl:value-of select="@name"/>
      </xsl:attribute>

      <xsl:for-each select="customer">
        <xsl:sort select="@name"/>
        <xsl:copy-of select="."/>
      </xsl:for-each>

    </contactGrp>
  </xsl:template>

</xsl:stylesheet>

谢谢。我知道XSLT是正确的方法,但是无法使XML按照正确的级别排序。谢谢。 - vondip

2

这个转换

<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="contactGrp">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*">
     <xsl:sort select="@name"/>
   </xsl:apply-templates>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

应用于提供的XML文档时:

<contactGrp name="People">
    <contactGrp name="Developers">
        <customer name="Mike" ></customer>
        <customer name="Brad" ></customer>
        <customer name="Smith" ></customer>
    </contactGrp>
    <contactGrp name="QA">
        <customer name="John" ></customer>
        <customer name="abi" ></customer>
    </contactGrp>
</contactGrp>

能够产生所需的、正确的结果:

<contactGrp name="People">
  <contactGrp name="Developers">
    <customer name="Brad" />
    <customer name="Mike" />
    <customer name="Smith" />
  </contactGrp>
  <contactGrp name="QA">
    <customer name="abi" />
    <customer name="John" />
  </contactGrp>
</contactGrp>

请注意:无论 contactGrp 元素的嵌套级别如何,都将产生正确的结果。


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