比较每个XML级别

时间:2016-06-07 13:53:38

标签: c# json xml

我正在使用C#开发一个应用,并尝试完成XML中的JSON。为了使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而不知道&#34;候选人&#34;节点名称。我需要这个可以接收的任何名字。

同样在这个例子中,XML只有2个级别,但它可以有任意数量的级别。我可以使用此函数迭代XML

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

如何进行比较并对节点进行分组?

2 个答案:

答案 0 :(得分:2)

一个相当天真的实现可以使用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

答案 1 :(得分:0)

这是一个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>

<强>限制

  • 它不会处理混合内容(带有子项和文字内容的元素)

  • 删除属性(容易修复)