【问题标题】:Compare Node Names in every level of XML比较每个级别的 XML 中的节点名称
【发布时间】:2016-06-07 13:53:37
【问题描述】:

我正在使用C# 开发一个应用程序,并尝试完成我从JSON 获得的XML。为了使XML 对我的应用程序有效,我需要将具有相同名称的元素分组到父元素下。 比如我得到了这个XML

<root>
<row>
    <id>0001</id>
    <type>credit</type>
    <investment>1000</investment>
    <ppr>0.83</ppr>
    <candidate>
        <id>5001</id>
        <name>Hugo</name>
    </candidate>
    <candidate>
        <id>5002</id>
        <name>Jack</name>
    </candidate>
    <candidate>
        <id>5005</id>
        <name>Kate</name>
    </candidate>
</row>

我需要将所有名称为候选的元素分组,在父节点候选下,像这样

<root>
<row>
    <id>0001</id>
    <type>credit</type>
    <investment>1000</investment>
    <ppr>0.83</ppr>
<candidates>
        <candidate>
                <id>5001</id>
                <name>Hugo</name>
        </candidate>
        <candidate>
                <id>5002</id>
                <name>Jack</name>
        </candidate>
        <candidate>
                <id>5005</id>
                <name>Kate</name>
        </candidate>
</candidates>
</row>

但这是我的问题:我不知道可以从 JSON 收到的名称。所以我需要在不知道“候选”节点名称的情况下进行此比较并完成 XML。任何我能收到的名字我都需要这个。

同样在本例中,XML 只有 2 个级别,但它可以有任意数量的级别。我可以毫无问题地遍历XML

public void findAllNodes(XmlNode node)
{ 
  Console.WriteLine(node.Name);
  foreach (XmlNode n in node.ChildNodes)
    findAllNodes(n); 
}

如何比较和分组节点?

【问题讨论】:

    标签: c# json xml


    【解决方案1】:

    一个相当幼稚的实现可以使用 LINQ 按名称对元素进行分组,并为组中包含多个项目的元素添加父元素。这将是递归的,因此元素的子元素被分组,直到树用完为止。

    天真之处在于,如果有混合的内容元素,这样的解决方案就会失败,并且它会将不是同级的元素分组(基本上,这两个问题都会导致事情以错误的顺序结束)。它应该会给您一个良好的开端,并且足以满足您的目的。

    private static IEnumerable<XElement> GroupElements(IEnumerable<XElement> elements)
    {
        var elementsByName = elements.GroupBy(x => x.Name);
    
        foreach (var grouping in elementsByName)
        {
            var transformed = grouping.Select(e =>
                new XElement(e.Name,
                    GroupElements(e.Elements()),
                    e.Attributes(),
                    e.Nodes().OfType<XText>()));
    
            if (grouping.Count() == 1)
            {
                yield return transformed.Single();
            }
            else
            {
                var groupName = grouping.Key + "s";
                yield return new XElement(groupName, transformed);
            }
        }
    }
    

    您可以通过解析/加载现有 XML,然后转换根元素并从中创建新文档来使用它:

    var original = XDocument.Parse(xml);
    var grouped = new XDocument(GroupElements(original.Elements()));
    

    请参阅this fiddle 以获得工作演示。

    【讨论】:

    • 非常感谢!这正是我所期待的
    【解决方案2】:

    这是一个 XSLT 2.0 解决方案:

    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
        <xsl:output indent="yes"/>
    
        <xsl:template match="*[*]">
            <xsl:copy>
                <xsl:for-each-group select="*" group-adjacent="node-name(.)">
                    <xsl:choose>
                        <xsl:when test="count(current-group()) > 1">
                            <xsl:element name="{name()}s" namespace="{namespace-uri()}">
                                <xsl:apply-templates select="current-group()"/>
                            </xsl:element>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:apply-templates select="current-group()"/>
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:for-each-group>
            </xsl:copy>
        </xsl:template>
        <xsl:template match="*">
            <xsl:copy-of select="."/>
        </xsl:template>
    </xsl:stylesheet>
    

    输出:

    <?xml version="1.0" encoding="UTF-8"?>
    <root>
       <row>
          <id>0001</id>
          <type>credit</type>
          <investment>1000</investment>
          <ppr>0.83</ppr>
          <candidates>
             <candidate>
                <id>5001</id>
                <name>Hugo</name>
             </candidate>
             <candidate>
                <id>5002</id>
                <name>Jack</name>
             </candidate>
             <candidate>
                <id>5005</id>
                <name>Kate</name>
             </candidate>
          </candidates>
       </row>
    </root>
    

    限制

    • 它不处理混合内容(带有子元素的元素加上文本内容)

    • 它会丢弃属性(很容易修复)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-07
      相关资源
      最近更新 更多