输入:
<root>
<name>a,b,c,d,e,f,g,h,i,j,k,</name>
<value>1,,3,,5,,7,,,,11,<value>
</root>
预期输出
<root>
<out>a:1|c:3|e:5|g:7|k:11</out>
<e>5</e>
<j/>
</root>
我能够通过递归获得期望。 但是我需要分别打印两个值,例如“ e”和“ j”。 循环时,如果名称是'e'或'j',则应创建这些元素。我无法做到这一点。
递归代码
<xsl:template match="//root">
<xsl:param name="columnName" select="a,b,c,d,e,f,g,h,i,j,k"></xsl:param>
<root>
<out>
<xsl:call-template name="merge">
<xsl:with-param name="name" select="normalize-space(name)" />
<xsl:with-param name="value" select="normalize-space(value)" />
</xsl:call-template>
</out>
</root>
</xsl:template>
<xsl:template name="merge">
<xsl:param name="name" />
<xsl:param name="value" />
<xsl:param name="separator" select="','" />
<xsl:variable name="currentValue" select="substring-before($value, $separator)"/>
<xsl:if test="$currentValue!=''">
<xsl:value-of select="substring-before($name, $separator)" /><xsl:text>:</xsl:text>
<xsl:value-of select="$currentValue" /><xsl:text>|</xsl:text>
</xsl:if>
<xsl:call-template name="merge">
<xsl:with-param name="value" select="normalize-space(substring-after($value, $separator))" />
<xsl:with-param name="name" select="normalize-space(substring-after($name, $separator))" />
</xsl:call-template>
</xsl:template>
如何在循环中添加两个元素以获取“退出”?
答案 0 :(得分:1)
另一种方法是将“合并”模板更改为<a>1</a>
形式的输出元素,可以将其存储在变量中并进行操作以获取所需的输出。
尝试使用此XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="//root">
<xsl:variable name="nodes">
<xsl:call-template name="merge">
<xsl:with-param name="name" select="normalize-space(name)" />
<xsl:with-param name="value" select="normalize-space(value)" />
</xsl:call-template>
</xsl:variable>
<root>
<out>
<xsl:value-of select="$nodes/*[normalize-space()]/concat(name(), ':', .)" separator="|" />
<!-- Alternate approach if above does not work
<xsl:for-each select="$nodes/*[normalize-space()]">
<xsl:if test="position() > 1">|</xsl:if>
<xsl:value-of select="concat(name(), ':', .)" />
</xsl:for-each>
-->
</out>
<xsl:copy-of select="$nodes/e" />
<xsl:copy-of select="$nodes/j" />
</root>
</xsl:template>
<xsl:template name="merge">
<xsl:param name="name" />
<xsl:param name="value" />
<xsl:param name="separator" select="','" />
<xsl:variable name="currentName" select="substring-before($name, $separator)"/>
<xsl:if test="$currentName!=''">
<xsl:element name="{$currentName}">
<xsl:value-of select="substring-before($value, $separator)" />
</xsl:element>
<xsl:call-template name="merge">
<xsl:with-param name="value" select="normalize-space(substring-after($value, $separator))" />
<xsl:with-param name="name" select="normalize-space(substring-after($name, $separator))" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
在http://xsltfiddle.liberty-development.net/6qVRKwD/1上查看它的运行情况
答案 1 :(得分:0)
当您从过程性思维出发时,很自然地用诸如“当我遍历值时,我会计算出以后需要作为副作用的其他东西”之类的术语来思考。通常,对于函数式编程,最好以不同的方式思考。独立计算输出中需要的每件事,不要尝试在输入的一次传递中计算几件事。
但是,您可以做的是将变量预先计算为输入数据的函数,如果它们将不只一次有用的话。因此,在XSLT 2.0中,您可以做到
<xsl:variable name="names" select="tokenize(name, ',')"/>
<xsl:variable name="values" select="tokenize(value, ',')"/>
然后您可以编写一个函数
<xsl:function name="f:value" as="xs:string">
<xsl:param name="key" as="xs:string"/>
<xsl:sequence select="$values[index-of($names, $key)]"/>
</xsl:function>
然后您可以这样做,例如:
<e><xsl:value-of select="f:value('e')"/></e>
<j><xsl:value-of select="f:value('j')"/></j>
如果可以使用XSLT 3.0,那么所有这些的自然解决方案是使用地图。像这样:
<xsl:variable name="names" select="tokenize(name, ',')"/>
<xsl:variable name="map" as="map(*)"
select="map:merge(
for-each-pair($names,
tokenize(value, ','),
function($k, $v) {map{$k, $v}}))"/>
</xsl:variable>
<out>
<xsl:value-of select="$names ! (. || ':' || $map(.))" separator="|"/>
</out>
<e>{$map?e}</e>
<j>{$map?j}</j>
答案 2 :(得分:0)
如果至少具有XSLT 2支持,则可以使用tokenize
并将两个值序列转换为某种XML,然后可以进一步处理:
<xsl:transform
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="2.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="root">
<xsl:copy>
<xsl:variable name="pairs">
<xsl:variable name="values" select="tokenize(value, ',')"/>
<xsl:for-each select="tokenize(name, ',')[. castable as xs:QName]">
<xsl:element name="{.}">
<xsl:variable name="pos" select="position()"/>
<xsl:value-of select="$values[$pos]"/>
</xsl:element>
</xsl:for-each>
</xsl:variable>
<out>
<xsl:value-of select="$pairs/(* except (e, j))[normalize-space()]/concat(name(), ':', .)" separator="|"/>
</out>
<xsl:copy-of select="$pairs/(e, j)"/>
</xsl:copy>
</xsl:template>
</xsl:transform>
http://xsltransform.hikmatu.com/gWcDMez
使用XSLT 3,您甚至可以将从tokenize
返回的字符串序列推到模板中以构造中间XML(尽管我不得不承认我喜欢使用迈克尔·凯已经发布了超过XML的使用):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match=".[. instance of xs:string]">
<xsl:param name="values"/>
<xsl:element name="{.}">{ let $pos := position() return $values[$pos] }</xsl:element>
</xsl:template>
<xsl:template match="root">
<xsl:copy>
<xsl:variable name="pairs">
<xsl:apply-templates select="tokenize(name, ',')[. castable as xs:QName]">
<xsl:with-param name="values" select="tokenize(value, ',')"/>
</xsl:apply-templates>
</xsl:variable>
<out>
<xsl:value-of select="$pairs/(* except (e, j))[normalize-space()]!(name() || ':' || .)" separator="|"/>
</out>
<xsl:copy-of select="$pairs/(e, j)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>