3

我正在尝试递归地展平/规范化下面的结构,但没有运气。

<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 -> CCC 和 BBB -> CCC。

4

4 回答 4

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. 正确使用参数 - 传递给模板。

于 2012-09-21T14:59:41.233 回答
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
于 2012-09-21T14:44:30.143 回答
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>
于 2012-09-21T14:51:21.897 回答
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

我希望它有所帮助。

于 2012-09-21T18:44:28.360 回答