【问题标题】:Flattening an XML document展平 XML 文档
【发布时间】:2012-03-02 04:25:00
【问题描述】:

我目前正在尝试在 C# 中展平深度结构化的 XML 文档,以便将元素的每个值都转换为属性。

XML结构如下:

<members>
    <member xmlns="mynamespace" id="1" status="1">
        <sensitiveData>
            <notes/>
            <url>someurl</url>
            <altUrl/>
            <date1>somedate</date1>
            <date2>someotherdate</date2>
            <description>some description</description>
            <tags/>
            <category>some category</category>
        </sensitiveData>
        <contacts>
            <contact contactId="1">
                <contactPerson>some contact person</contactPerson>
                <phone/>
                <mobile>mobile number</mobile>
                <email>some@email.com</email>
            </contact>
        </contacts>
    </member>
</members>

我希望它看起来像这样:

<members>
    <member xmlns="mynamespace" id="1" status="1" notes="" url="someurl" altUrl="" date1="somedate" date2="someotherdate" description="some description" tags="" category="some category" contactId="1" contactPerson="some contact person" phone="" mobile="mobile number" email="some@email.com" />
</members>

我可以只解析元素名称及其属性,但由于这个 XML 来自我无法控制的 web 服务,我必须创建某种动态解析器​​来将其展平,因为结构 可以 在某个时候改变。

值得注意的是,XML 结构来自 web 服务的 XElement。

以前有没有人尝试过这样做,并且可以帮助分享一下方法? :-) 将不胜感激!

非常感谢。

一切顺利,

【问题讨论】:

  • 您显示的 XML 无效(/kontakter 没有开始标签?)...至于您的问题,没有一般答案,因为这完全取决于您要应用的规则(例如如果有多个contact 等会发生什么)。我的问题是:你为什么要“扁平化”这个 XML?
  • 嗨 Yahia,谢谢 - 这只是一个错字 :) 我需要将其展平才能将其导入 CMS。
  • 那么恕我直言,XSLT 是可行的方法,因为它允许在部署后更改转换规则...

标签: c# xml parsing xml-parsing


【解决方案1】:

试试这个:

var doc = XDocument.Parse(@"<members>...</members>");

var result = new XDocument(
    new XElement(doc.Root.Name,
        from x in doc.Root.Elements()
        select new XElement(x.Name,
            from y in x.Descendants()
            where !y.HasElements
            select new XAttribute(y.Name.LocalName, y.Value))));

结果:

<members>
  <member notes="" url="someurl" altUrl="" date1="somedate" date2="someotherdate" description="some description" tags="" category="some category" contactPerson="some contact person" phone="" mobile="mobile number" email="some@email.com" xmlns="mynamespace" />
</members>

【讨论】:

    【解决方案2】:

    您可以使用这个 XSLT 1.0 样式表。您可能想要修改它处理多个 &lt;contact&gt; 元素的方式。

    输入 XML

    <members>
      <member xmlns="mynamespace" id="1" status="1">
        <sensitiveData>
          <notes/>
          <url>someurl</url>
          <altUrl/>
          <date1>somedate</date1>
          <date2>someotherdate</date2>
          <description>some description</description>
          <tags/>
          <category>some category</category>
        </sensitiveData>
        <contacts>
          <contact contactId="1">
            <contactPerson>some contact person</contactPerson>
            <phone/>
            <mobile>mobile number</mobile>
            <email>some@email.com</email>
          </contact>
          <contact contactId="2">
            <contactPerson>second contact person</contactPerson>
            <phone/>
            <mobile>second mobile number</mobile>
            <email>second some@email.com</email>
          </contact>
        </contacts>
      </member>
    </members>
    

    XSLT 1.0

    <xsl:stylesheet version="1.0" xmlns:my="mynamespace" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output indent="yes"/>
      <xsl:strip-space elements="*"/>
    
      <xsl:template match="node()|@*">
        <xsl:apply-templates select="node()|@*"/>
      </xsl:template>
    
      <xsl:template match="members|my:member">
        <xsl:copy>
          <xsl:apply-templates select="node()|@*"/>  
        </xsl:copy>
      </xsl:template>
    
      <xsl:template match="node()[text()][ancestor::my:member]|@*[ancestor::my:member]">
        <xsl:variable name="vContact">
          <xsl:if test="ancestor-or-self::my:contact">
            <xsl:value-of select="count(ancestor-or-self::my:contact/preceding-sibling::my:contact) + 1"/>
          </xsl:if>
        </xsl:variable>
        <xsl:attribute name="{name()}{$vContact}">
          <xsl:value-of select="."/>
        </xsl:attribute>
      </xsl:template>
    
    </xsl:stylesheet>
    

    XML 输出

    <members>
       <member xmlns="mynamespace" id="1" status="1" url="someurl" date1="somedate"
               date2="someotherdate"
               description="some description"
               category="some category"
               contactId1="1"
               contactPerson1="some contact person"
               mobile1="mobile number"
               email1="some@email.com"
               contactId2="2"
               contactPerson2="second contact person"
               mobile2="second mobile number"
               email2="second some@email.com"/>
    </members>
    

    【讨论】:

      【解决方案3】:

      我认为 dtb 答案是最好的方法。但是,您必须注意一个重要问题。尝试添加另一个联系信息,dtb 代码会崩溃。因为一个成员可以有多个联系信息,但不能有重复的属性。为了解决这个问题,我更新了代码以仅选择不同的属性。为此,我实施了IEqualityComparer&lt;XAttribute&gt;。 更新后的 linq 表达式如下所示

      var result = new XDocument(new XElement(doc.Root.Name, 
                      from x in doc.Root.Elements() 
                      select new XElement(x.Name, (from y in x.Descendants() 
                                                  where !y.HasElements
                                                  select new XAttribute(y.Name.LocalName, y.Value)).Distinct(new XAttributeEqualityComparer())
                                                  )));
      

      如您所见,添加了一个带有自定义平等比较器重载(XAttributeEqualityComparer) 的 Distinct 调用

          class XAttributeEqualityComparer : IEqualityComparer<XAttribute>
          {
              public bool Equals(XAttribute x, XAttribute y)
              {
                  return x.Name == y.Name; 
              }
      
              public int GetHashCode(XAttribute obj)
              {
                  return obj.Name.GetHashCode(); 
              }
          }
      

      【讨论】:

        【解决方案4】:

        您可以编写一个 XSLT 转换来将元素转换为属性。

        【讨论】:

          【解决方案5】:

          您这样做是为了创建另一个 XML 文档,还是只是为了让您的处理更简单?如果是前者,那么当您遇到叶节点时,您只需将所有值放入映射中即可。然后,您实际上可以遍历映射中的键值对以仅使用属性重构 xml 标记。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-05-23
            • 2017-10-01
            • 1970-01-01
            • 1970-01-01
            • 2019-12-13
            • 2012-01-07
            • 2017-05-31
            • 2011-04-09
            相关资源
            最近更新 更多