XSLT 1.0:递归地展平/非规范化结构

时间:2012-09-21 13:29:50

标签: xslt recursion

我试图递归地压扁/标准化下面的结构而没有运气。

<models>
  <model name="AAA" root="true">
    <items>
        <item name="a"/>
        <item name="b"/>
    </items>
    <submodels>
        <submodel ref="BBB"/>
        <submodel ref="CCC" />
    </submodels>
  </model>
  <model name="BBB">
    <items>
        <item name="c"/>
        <item name="d"/>
    </items>
    <submodels>
        <submodel ref="CCC" />
    </submodels>
  </model>
  <model name="CCC">
    <item name="e" />
  </model>
</models>

预期结果如下:

/AAA
/AAA/a
/AAA/b
/AAA/BBB
/AAA/BBB/c
/AAA/BBB/d
/AAA/BBB/CCC
/AAA/BBB/CCC/e
/AAA/CCC
/AAA/CCC/e

我尝试过以递归方式使用。但主要问题是几个模型可以引用单个模型。例如。 AAA - &gt; CCC和BBB - &gt; CCC

4 个答案:

答案 0 :(得分:1)

这个简短而简单的转型

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

  <xsl:key name="kmodelByName" match="model" use="@name"/>
  <xsl:key name="ksubmodelByRef" match="submodel" use="@ref"/>

  <xsl:template match="/*">
   <xsl:apply-templates select="model[not(key('ksubmodelByRef', @name))]"/>
  </xsl:template>

    <xsl:template match="model|item">
      <xsl:param name="pPath"/>
      <xsl:value-of select="concat('&#xA;', $pPath, '/', @name)"/>
      <xsl:apply-templates select="item|*/item|*/submodel">
       <xsl:with-param name="pPath" select="concat($pPath, '/', @name)"/>
      </xsl:apply-templates>
    </xsl:template>

    <xsl:template match="submodel">
      <xsl:param name="pPath"/>
      <xsl:apply-templates select="key('kmodelByName', @ref)">
        <xsl:with-param name="pPath" select="$pPath"/>
      </xsl:apply-templates>
    </xsl:template>
</xsl:stylesheet>

应用于提供的XML文档

<models>
    <model name="AAA" root="true">
        <items>
            <item name="a"/>
            <item name="b"/>
        </items>
        <submodels>
            <submodel ref="BBB"/>
            <submodel ref="CCC" />
        </submodels>
    </model>
    <model name="BBB">
        <items>
            <item name="c"/>
            <item name="d"/>
        </items>
        <submodels>
            <submodel ref="CCC" />
        </submodels>
    </model>
    <model name="CCC">
        <item name="e"/>
    </model>
</models>

生成想要的正确结果

/AAA
/AAA/a
/AAA/b
/AAA/BBB
/AAA/BBB/c
/AAA/BBB/d
/AAA/BBB/CCC
/AAA/BBB/CCC/e
/AAA/CCC
/AAA/CCC/e

<强>解释

  1. 正确使用密钥可以简化转换,轻松表达和高效

  2. 正确使用模板。

  3. 正确使用参数 - 传递给模板。

答案 1 :(得分:0)

首先,我怀疑你想要数学标记为根的模型

<xsl:apply-templates select="model[@root='true']" />

然后你会有一个模板来匹配模型元素,但是一个带有包含父模型当前“路径”的参数的模板

<xsl:template match="model">
   <xsl:param name="path" />

您可以像这样输出完整路径

<xsl:variable name="newpath" select="concat($path, '/', @name)" />
<xsl:value-of select="concat($newpath, '&#13;')" />

然后,您可以输出,但将新路径作为参数传递

<xsl:apply-templates select="items/item|item">
   <xsl:with-param name="path" select="$newpath" />
</xsl:apply-templates>

为了匹配子模型,理想情况下可以使用密钥

<xsl:key name="models" match="model" use="@name" />

然后你可以像这样匹配子模型

<xsl:apply-templates select="key('models', submodels/submodel/@ref)">
   <xsl:with-param name="path" select="$newpath" />
</xsl:apply-templates>

这将递归匹配相同的模型模板。

这是完整的XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="text" indent="yes"/>
   <xsl:key name="models" match="model" use="@name" />

   <xsl:template match="/models">
      <xsl:apply-templates select="model[@root='true']" />
   </xsl:template>

   <xsl:template match="model">
      <xsl:param name="path" />

      <xsl:variable name="newpath" select="concat($path, '/', @name)" />
      <xsl:value-of select="concat($newpath, '&#13;')" />

      <xsl:apply-templates select="items/item|item">
         <xsl:with-param name="path" select="$newpath" />
      </xsl:apply-templates>

      <xsl:apply-templates select="key('models', submodels/submodel/@ref)">
         <xsl:with-param name="path" select="$newpath" />
      </xsl:apply-templates>

   </xsl:template>

   <xsl:template match="item">
      <xsl:param name="path" />
      <xsl:value-of select="concat($path, '/', @name, '&#13;')" />
   </xsl:template>
</xsl:stylesheet>

当应用于您的示例XML时,输出以下内容

/AAA
/AAA/a
/AAA/b
/AAA/BBB
/AAA/BBB/c
/AAA/BBB/d
/AAA/BBB/CCC
/AAA/BBB/CCC/e
/AAA/CCC
/AAA/CCC/e

答案 2 :(得分:0)

我不确定你的转型规则是什么。这是一个猜测,但下面的XSLT 1.0样式表确实将您提供的输入转换为预期的输出。

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

<xsl:template match="/">
  <xsl:apply-templates select="models/model[@root='true']">
    <xsl:with-param name="base" select="''" />
  </xsl:apply-templates>  
</xsl:template>

<xsl:template match="model|item">
  <xsl:param name="base" />
  <xsl:variable name="new-base" select="concat($base,'/',@name)" />
  <xsl:value-of select="concat($new-base,'&#x0A;')" />
  <xsl:apply-templates select="items/item | item | submodels/submodel/@ref">
    <xsl:with-param name="base" select="$new-base" />
  </xsl:apply-templates>  
</xsl:template>

<xsl:template match="@ref">
  <xsl:param name="base" />
  <xsl:apply-templates select="../../../../model[@name=current()]">
    <xsl:with-param name="base" select="$base" />
  </xsl:apply-templates>  
</xsl:template>

</xsl:stylesheet>

答案 3 :(得分:0)

可能有一种更简单的方法,但我能够通过以下xslt获得您想要的输出:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" exclude-result-prefixes="exsl" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common">

    <xsl:variable name="root" select="/models/model[@root = 'true']/@name"/>

    <xsl:template match="models">
        <xsl:for-each select="model">

            <xsl:variable name="savedNode" select="."/>

            <xsl:variable name="precedingValue">
                <xsl:call-template name="preceding">
                    <xsl:with-param name="modelName" select="@name"/>
                </xsl:call-template>
            </xsl:variable>

            <xsl:if test="not(exsl:node-set($precedingValue)/preceding)">
                <xsl:call-template name="model">
                    <xsl:with-param name="node" select="$savedNode"/>
                </xsl:call-template>
            </xsl:if>            

            <xsl:for-each select="exsl:node-set($precedingValue)/preceding">
                <xsl:sort select="position()" data-type="number" order="descending"/>
                <xsl:variable name="precedingTmp" select="normalize-space(.)"/>
                <xsl:variable name="normalizedPreceding" select="translate($precedingTmp, ' ', '')"/>  

                <xsl:call-template name="model">
                    <xsl:with-param name="node" select="$savedNode"/>
                    <xsl:with-param name="preceding" select="$normalizedPreceding"/>
                </xsl:call-template>
            </xsl:for-each>

        </xsl:for-each>
    </xsl:template>

    <xsl:template name="model">
        <xsl:param name="node"/>
        <xsl:param name="preceding"/>
        \<xsl:value-of select="$preceding"/><xsl:value-of select="$node/@name"/>
        <xsl:for-each select="$node/items/item">
            \<xsl:value-of select="$preceding"/><xsl:value-of select="$node/@name"/>\<xsl:value-of select="@name"/>
        </xsl:for-each>
    </xsl:template>

    <xsl:template name="preceding">
        <xsl:param name="modelName"/>

        <xsl:for-each select="preceding::model">
            <xsl:for-each select="submodels/submodel">
                <xsl:choose>
                    <xsl:when test="contains(@ref, $modelName)">
                        <preceding>
                            <xsl:call-template name="preceding">
                                <xsl:with-param name="modelName" select="$modelName"></xsl:with-param>
                            </xsl:call-template>
                            <xsl:value-of select="../../@name"/>\
                        </preceding>
                    </xsl:when>
                </xsl:choose>    
            </xsl:for-each>
        </xsl:for-each>
    </xsl:template>

    <xsl:template match="items">
        <xsl:for-each select="item">

        </xsl:for-each>
    </xsl:template>


</xsl:stylesheet>

输入

<models>
    <model name="AAA" root="true">
        <items>
            <item name="a"/>
            <item name="b"/>
        </items>
        <submodels>
            <submodel ref="BBB"/>
            <submodel ref="CCC" />
        </submodels>
    </model>
    <model name="BBB">
        <items>
            <item name="c"/>
            <item name="d"/>
        </items>
        <submodels>
            <submodel ref="CCC" />
        </submodels>
    </model>
    <model name="CCC">
        <items>
        <item name="e"/>
        </items>
    </model>
</models>

输出

\AAA
\AAA\a
\AAA\b
\AAA\BBB
\AAA\BBB\c
\AAA\BBB\d
\AAA\BBB\CCC
\AAA\BBB\CCC\e
\AAA\CCC
\AAA\CCC\e

我希望它有所帮助。