展平XML文档

时间:2012-02-09 17:00:46

标签: c# xml parsing xml-parsing

我目前正在尝试在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。

之前有没有人试图这样做,并有助于分享如何? :-)非常感谢!

提前多多感谢。

一切顺利,

5 个答案:

答案 0 :(得分:3)

试试这个:

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>

答案 1 :(得分:2)

我认为dtb回答是最好的方法。但是,您必须注意一个重要问题。尝试添加其他联系信息,dtb代码会崩溃。因为成员可以拥有多个联系人信息但又不能具有重复的属性。为了解决这个问题,我更新了代码以仅选择不同的属性。为此,我实施了IEqualityComparer<XAttribute>。 更新的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())
                                            )));

您可以注意到,添加了一个带有自定义Equality比较器重载的{6}的不同调用

(XAttributeEqualityComparer)

答案 2 :(得分:1)

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

答案 3 :(得分:1)

您可以使用此XSLT 1.0样式表。您可能想要修改它处理多个<contact>元素的方式。

输入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>

答案 4 :(得分:0)

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