我试图递归地压扁/标准化下面的结构而没有运气。
<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
答案 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('
', $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 :(得分: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, ' ')" />
然后,您可以输出项,但将新路径作为参数传递
<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, ' ')" />
<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, ' ')" />
</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,'
')" />
<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
我希望它有所帮助。