使用ref属性展平xml文件

时间:2010-11-24 12:34:26

标签: c# xml xslt

我想使用C#以编程方式压缩xml数据文件(请注意,这不是架构,.xsd,文件)(因此外部xml编辑器不起作用,除非它有API)。对于示例树结构:

<root>
    <A>
        <B att="val">
            <C>
                someData
            </C>
        </B>
    </A>
    <A>
        <B>
             someOtherData
        </B>
        <B>
            moreData
        </B>
    </A>
</root>

我想把它压平为:

<root>
    <A>
        <B ref="b1" />
    </A>
    <A>
        <B ref="b2" />
        <B ref="b3" />
    </A>
    <B id="b1" att="val">
         <C ref="c1" />
    </B>
    <B id="b2">
        someOtherData
    </B>
    <B id="b3">
        moreData
    </B>
    <C id="c1">
         someData
    </C>
</root>

有没有办法用C#实现这个目标?

有没有办法将扁平xml转换回树形结构?我想要尽可能通用的东西,所以任何xml文件都可以扁平化。

similar question on so,但它不涉及参考。

3 个答案:

答案 0 :(得分:3)

此样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:variable name="vUppercase" select="'QWERTYUIOPASDFGHJKLZXCVBNM'"/>
    <xsl:variable name="vLowercase" select="'qwertyuiopasdfghjklzxcvbnm'"/>
    <xsl:template match="*">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:if test="parent::*/parent::*">
                <xsl:attribute name="id">
                    <xsl:value-of select="translate(name(),
                                                    $vUppercase,
                                                    $vLowercase)"/>
                    <xsl:number level="any"/>
                </xsl:attribute>
            </xsl:if>
            <xsl:apply-templates mode="ref"/>
        </xsl:copy>
        <xsl:apply-templates/>
    </xsl:template>
    <xsl:template match="*" mode="ref">
        <xsl:copy>
            <xsl:attribute name="ref">
                <xsl:value-of select="translate(name(),
                                                $vUppercase,
                                                $vLowercase)"/>
                <xsl:number level="any"/>
            </xsl:attribute>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="text()"/>
    <xsl:template match="/*">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

输出:

<root>
    <A>
        <B ref="b1" />
    </A>
    <B att="val" id="b1">
        <C ref="c1" />
    </B>
    <C id="c1">
                    someData
    </C>
    <A>
        <B ref="b2" />
        <B ref="b3" />
    </A>
    <B id="b2">
                 someOtherData
    </B>
    <B id="b3">
                moreData
    </B>
</root>

反向样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="kElementById" match="*[@id]" use="@id"/>
    <xsl:key name="kElementByRef" match="*[@ref]" use="@ref"/>
    <xsl:template match="node()|@*" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*[key('kElementByRef',@id)]|
                         *[key('kElementByRef',@id)]/@id"/>
    <xsl:template match="*[@ref]">
        <xsl:for-each select="key('kElementById',@ref)">
            <xsl:call-template name="identity"/>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

输出:

<root>
    <A>
        <B att="val">
            <C>
                someData
            </C>
        </B>
    </A>
    <A>
        <B>
             someOtherData
        </B>
        <B>
            moreData
        </B>
    </A>
</root>

答案 1 :(得分:3)

此转化

<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:variable name="Lower" select=
  "'abcdefghijklmnopqrstuvwxyz'"
  />

 <xsl:variable name="vUpper" select=
  "'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"
  />

 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="/*">
  <root>
    <xsl:apply-templates select="node()"/>
    <xsl:apply-templates select="/*/*//*" mode="extract">
     <xsl:sort select="count(ancestor::*)" data-type="number"/>
    </xsl:apply-templates>
  </root>
 </xsl:template>

 <xsl:template match="*[ancestor::*[2]]">
   <xsl:variable name="vPos">
     <xsl:number level="any"/>
   </xsl:variable>

   <xsl:element name="{name()}">
     <xsl:attribute name="ref">
       <xsl:value-of select=
        "concat(translate(name(),$vUpper,$Lower),$vPos)"/>
     </xsl:attribute>
   </xsl:element>
 </xsl:template>

 <xsl:template match="*" mode="extract">
  <xsl:variable name="vPos">
   <xsl:number level="any"/>
  </xsl:variable>

  <xsl:element name="{name()}">
    <xsl:attribute name="id">
       <xsl:value-of select=
        "concat(translate(name(),$vUpper,$Lower),$vPos)"/>
    </xsl:attribute>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:element>
 </xsl:template>
</xsl:stylesheet>

应用于提供的XML文档

<root>
    <A>
        <B att="val">
            <C>
                someData
            </C>
        </B>
    </A>
    <A>
        <B>
             someOtherData
        </B>
        <B>
            moreData
        </B>
    </A>
</root>

生成完全想要的正确结果

<root>
   <A>
      <B ref="b1"/>
   </A>
   <A>
      <B ref="b2"/>
      <B ref="b3"/>
   </A>
   <B id="b1" att="val">
      <C ref="c1"/>
   </B>
   <B id="b2">
             someOtherData
        </B>
   <B id="b3">
            moreData
        </B>
   <C id="c1">
                someData
            </C>
</root>

逆向转换是

<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:key name="kElbyId" match="*" use="@id"/>

 <xsl:template match="node()|@*">
   <xsl:copy>
    <xsl:apply-templates select="node()|@*"/>
   </xsl:copy>
 </xsl:template>

 <xsl:template match="*[@ref]">
  <xsl:apply-templates mode="deepen"
       select="key('kElbyId',@ref)"/>
 </xsl:template>

 <xsl:template match="*[@id]"/>
 <xsl:template match="*[@id]" mode="deepen">
  <xsl:copy>
   <xsl:apply-templates
        select="@*[not(name()='id')] | node()"/>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

当对上面的展平转换结果应用逆向转换时,会生成初始XML文档

<root>
   <A>
      <B att="val">
         <C>
                someData
            </C>
      </B>
   </A>
   <A>
      <B>
             someOtherData
        </B>
      <B>
            moreData
        </B>
   </A>
</root>

答案 2 :(得分:2)