使用XSLT将xml层次化为Flat xml

时间:2011-02-25 07:01:45

标签: sql-server sql-server-2008 xslt reporting-services

我有xml,结构如下

<root>
  <PNode>
    <node1>
      <node1Child>data</node1Child>
      <node2Child>data</node2Child>
    </node1>
  </PNode>
  <SecondNode>
    <node1>
      <node1Child>
        <child>data</child>
      </node1Child>
    </node1>
  </SecondNode>
</root>

我想使用genric xslt输出,因为我有很多xml可以转换为这种格式。

<root>
  <Pnode-node1-node1Child>data</Pnode-node1-node1Child>
  <Pnode-node1-node2Child>data</Pnode-node1-node2Child>
  <SecondNode-node1-node1child-child>data</SecondNode-node1-node1child-child>
</root>

可能更深或更少 我可以通过XSLT来做,请给出任何示例或参考

我想这样做是为了从sql server 2k8 r2 rdl生成PDF。因为rdl不接受嵌套的xml所以需要将其展平。

4 个答案:

答案 0 :(得分:3)

此样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:template match="/*">
        <xsl:copy>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*">
        <xsl:param name="pName"/>
        <xsl:apply-templates>
            <xsl:with-param name="pName" select="concat($pName,name(),'-')"/>
        </xsl:apply-templates>
    </xsl:template>
    <xsl:template match="text()">
        <xsl:param name="pName"/>
        <xsl:element name="{substring($pName,1,string-length($pName)-1)}">
            <xsl:value-of select="."/>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

输出:

<root>
    <PNode-node1-node1Child>data</PNode-node1-node1Child>
    <PNode-node1-node2Child>data</PNode-node1-node2Child>
    <SecondNode-node1-node1Child-child>data</SecondNode-node1-node1Child-child>
</root>

更新:如果可能有empy节点......

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:template match="/*">
        <xsl:copy>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*">
        <xsl:param name="pName"/>
        <xsl:apply-templates>
            <xsl:with-param name="pName" select="concat($pName,name(),'-')"/>
        </xsl:apply-templates>
    </xsl:template>
    <xsl:template match="*[not(*)]">
        <xsl:param name="pName"/>
        <xsl:element name="{$pName}{name()}">
            <xsl:value-of select="."/>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

注意:匹配最内层元素。

答案 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:template match="/*">
     <root>
      <xsl:apply-templates/>
     </root>
 </xsl:template>

 <xsl:template match="text()">
  <xsl:variable name="vCompName">
   <xsl:for-each select="ancestor::*[not(position() =last())]">
    <xsl:value-of select="translate(name(), ':', '_')"/>
    <xsl:if test="not(position()=last())">-</xsl:if>
   </xsl:for-each>
  </xsl:variable>

  <xsl:element name="{$vCompName}">
   <xsl:value-of select="."/>
  </xsl:element>
 </xsl:template>
</xsl:stylesheet>

应用于提供的XML文档时:

<root>
    <PNode>
        <node1>
            <node1Child>data</node1Child>
            <node2Child>data</node2Child>
        </node1>
    </PNode>
    <SecondNode>
        <node1>
            <node1Child>
                <child>data</child>
            </node1Child>
        </node1>
    </SecondNode>
</root>

会产生想要的正确结果:

<root>
   <PNode-node1-node1Child>data</PNode-node1-node1Child>
   <PNode-node1-node2Child>data</PNode-node1-node2Child>
   <SecondNode-node1-node1Child-child>data</SecondNode-node1-node1Child-child>
</root>

<强>解释

  1. 除了将文档包装在root顶部元素中之外,只有一个模板。它匹配任何非空白文本节点。

  2. 除了文档顺序中的第一个元素祖先(反向轴ancestor::中的最后一个)之外的所有元素祖先的节点集都使用'-'字符和元素进行字符串连接用这个字符串作为名称构造。

  3. 在上面的2.中的字符串连接操作之前,每个名称都被修改,以便其中的任何':'字符被下划线字符替换。如果某些名称中存在名称空间前缀,则这使得转换不会产生无效的复合名称。

  4. 最后,将当前文本节点复制为动态构造元素的子节点。

答案 2 :(得分:1)

鉴于此输入:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <PNode>
        <node1>
            <node1Child>data</node1Child>
            <node2Child>data</node2Child>
        </node1>
    </PNode>
    <SecondNode>
        <node1>
            <node1Child>
                <child>data</child>
            </node1Child>
        </node1>
    </SecondNode>
</root>

以下样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:strip-space elements="*"/>
    <xsl:output indent="yes" method="xml"/>

    <xsl:template match="root">
        <xsl:copy>
            <xsl:apply-templates select="*" mode="flatten"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*[normalize-space(text())]" mode="flatten">
        <xsl:param name="name-prefix" select="''"/>
        <xsl:variable name="name">
            <xsl:call-template name="construct-name">
                <xsl:with-param name="name-prefix" select="$name-prefix"/>
            </xsl:call-template>
        </xsl:variable>

        <xsl:element name="{$name}">
            <xsl:apply-templates select="text()"/>
        </xsl:element>

        <xsl:apply-templates select="node()" mode="flatten">
            <xsl:with-param name="name-prefix" select="$name"/>
        </xsl:apply-templates>
    </xsl:template>

    <xsl:template match="*[not(normalize-space(text()))]" mode="flatten">
        <xsl:param name="name-prefix" select="''"/>

        <xsl:variable name="prefix">
            <xsl:call-template name="construct-name">
                <xsl:with-param name="name-prefix" select="$name-prefix"/>
            </xsl:call-template>
        </xsl:variable>

        <xsl:apply-templates select="node()" mode="flatten">
            <xsl:with-param name="name-prefix" select="$prefix"/>
        </xsl:apply-templates>
    </xsl:template>

    <xsl:template name="construct-name">
        <xsl:param name="name-prefix"/>
        <xsl:choose>
            <xsl:when test="$name-prefix">
                <xsl:value-of select="concat($name-prefix, '-', local-name(.))"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="local-name(.)"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template match="text()" mode="flatten"/>
</xsl:stylesheet>

产生想要的结果:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <PNode-node1-node1Child>data</PNode-node1-node1Child>
    <PNode-node1-node2Child>data</PNode-node1-node2Child>
    <SecondNode-node1-node1Child-child>data</SecondNode-node1-node1Child-child>
</root>

答案 3 :(得分:0)

在模板中,测试子节点。

如果有子节点,则将前一个参数值传递给该元素的名称。

如果只有#text,则使用参数作为名称输出新元素,并将其内容设置为#text