我正在使用XSLT 1.0 输入xml文档:
<VirtualHost *:443>
</VirtualHost>
预期产出:
<VirtualHost 10.10.10.1:443 [1:2:3:4]:443>
</VirtualHost>
我在XSLT 1.0中实现了tokenize函数的通用解决方案,如下所述:http://www.heber.it/?p=1088
目前的实施:
<Table>
<Numbers>10,100,1000</Numbers>
<Values>
<Value>1</Value>
<Value>2</Value>
<Value>3</Value>
</Values>
</Table>
当前输出:
<Table>
<Results>
<Values>
<Value>1</Value>
<Number>10</Number>
</Values>
<Values>
<Value>2</Value>
<Number>100</Number>
</Values>
<Values>
<Value>3</Value>
<Number>1000</Number>
</Values>
</Results>
</Table>
但输出不正确。我无法获得,在应用tokenize模板后,如何以正确的顺序返回结果,但不是每个Value节点的所有结果都返回结果。
很清楚如何使用XSLT 2.0实现相同的功能,因为我可以执行以下操作:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:template match="/">
<table name="Results">
<xsl:variable name="Numbers">
<xsl:value-of select="Table/Numbers"/>
</xsl:variable>
<Results>
<xsl:for-each select="Table/Values/Value">
<Values>
<xsl:variable name="Value" select="."/>
<xsl:variable name="n" select="count(preceding-sibling::*)+1."/>
<Value>
<xsl:value-of select="$Value"/>
</Value>
<xsl:call-template name="tokenizeString">
<xsl:with-param name="list" select="$Numbers"/>
<xsl:with-param name="delimiter" select="','"/>
</xsl:call-template>
</Values>
</xsl:for-each>
</Results>
</table>
</xsl:template>
<xsl:template name="tokenizeString">
<!--passed template parameter -->
<xsl:param name="list"/>
<xsl:param name="delimiter"/>
<xsl:choose>
<xsl:when test="contains($list, $delimiter)">
<!-- get everything in front of the first delimiter -->
<Number>
<xsl:value-of select="substring-before($list,$delimiter)"/>
</Number>
<xsl:call-template name="tokenizeString">
<!-- store anything left in another variable -->
<xsl:with-param name="list" select="substring-after($list,$delimiter)"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<Number>
<xsl:value-of select="$list"/>
</Number>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
更新:使用节点集的解决方案对我来说很好,可以使用XSLT1来表示tokenize函数。但我得到了另一个案例,这是初始要求的补充。情况是只有一个数字对应一组值:
<table xmlns:fo="http://www.w3.org/1999/XSL/Format" name="Results">
<Results>
<Values>
<Value>1</Value>
<Number>10</Number>
<Number>100</Number>
<Number>1000</Number>
</Values>
<Values>
<Value>2</Value>
<Number>10</Number>
<Number>100</Number>
<Number>1000</Number>
</Values>
<Values>
<Value>3</Value>
<Number>10</Number>
<Number>100</Number>
<Number>1000</Number>
</Values>
</Results>
</table>
预期产出:
<xsl:variable name"Foo" select="tokenize($string,$delimter)[$n]">
我的愿景是给michael-hor257k添加以下条件:
<Table>
<Foo>
<Numbers>10,100,1000</Numbers>
<Values>
<Value>1</Value>
<Value>2</Value>
<Value>3</Value>
</Values>
</Foo>
<Foo>
<Numbers>10</Numbers>
<Values>
<Value>4</Value>
<Value>5</Value>
<Value>6</Value>
</Values>
</Foo>
</Table>
这看起来合情合理还是我可以避免硬编码并将此类案例放在模板中?
答案 0 :(得分:1)
以这种方式尝试:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/Table">
<xsl:variable name="Numbers">
<xsl:call-template name="tokenizeString">
<xsl:with-param name="list" select="Numbers"/>
<xsl:with-param name="delimiter" select="','"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="numbers-set" select="exsl:node-set($Numbers)/Number" />
<table name="Results">
<Results>
<xsl:for-each select="Values/Value">
<xsl:variable name="i" select="position()" />
<Values>
<Value>
<xsl:value-of select="."/>
</Value>
<Number>
<xsl:value-of select="$numbers-set[$i]"/>
</Number>
</Values>
</xsl:for-each>
</Results>
</table>
</xsl:template>
<xsl:template name="tokenizeString">
<xsl:param name="list"/>
<xsl:param name="delimiter"/>
<xsl:choose>
<xsl:when test="contains($list, $delimiter)">
<Number>
<xsl:value-of select="substring-before($list,$delimiter)"/>
</Number>
<xsl:call-template name="tokenizeString">
<xsl:with-param name="list" select="substring-after($list,$delimiter)"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<Number>
<xsl:value-of select="$list"/>
</Number>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
或者更短一些:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/Table">
<xsl:variable name="numbers">
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="Numbers"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="numbers-set" select="exsl:node-set($numbers)/Number" />
<xsl:copy>
<Results>
<xsl:for-each select="Values/Value">
<xsl:variable name="i" select="position()" />
<Values>
<xsl:copy-of select="."/>
<xsl:copy-of select="$numbers-set[$i]"/>
</Values>
</xsl:for-each>
</Results>
</xsl:copy>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="text"/>
<xsl:param name="delimiter" select="','"/>
<xsl:variable name="token" select="substring-before(concat($text, $delimiter), $delimiter)" />
<xsl:if test="$token">
<Number>
<xsl:value-of select="$token"/>
</Number>
</xsl:if>
<xsl:if test="contains($text, $delimiter)">
<!-- recursive call -->
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="substring-after($text, $delimiter)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
答案 1 :(得分:1)
FWIW,这是一个纯粹的XSLT 1.0解决方案,不需要node-set()
扩展功能:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/Table">
<xsl:copy>
<Results>
<xsl:call-template name="process">
<xsl:with-param name="values" select="Values/Value"/>
<xsl:with-param name="numbers" select="Numbers"/>
</xsl:call-template>
</Results>
</xsl:copy>
</xsl:template>
<xsl:template name="process">
<xsl:param name="values" select="dummy-node"/>
<xsl:param name="numbers"/>
<xsl:param name="i" select="1"/>
<!-- output -->
<Values>
<xsl:copy-of select="$values[1]"/>
<Number>
<xsl:value-of select="substring-before(concat($numbers, ','), ',')"/>
</Number>
</Values>
<!-- recursive call -->
<xsl:if test="count($values) > 1">
<xsl:call-template name="process">
<xsl:with-param name="values" select="$values[position() > 1]"/>
<xsl:with-param name="numbers" select="substring-after($numbers, ',')"/>
<xsl:with-param name="i" select="$i + 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
答案 2 :(得分:0)
通常,主要问题是XSLT 1.0中的模板返回结果树片段并在其上应用XPath,首先需要将其转换为节点集,这通常是调用exsl:node-set($result-tree-fragment)
或者您的XSLT 1.0处理器提供的类似扩展功能。
假设你有例如。
<xsl:variable name="tokens-rtf">
<xsl:call-template name="tokenize">...</xsl:call-template>
</xsl:variable>
模板返回的地方,例如
<Values>
<Value>1</Value>
<Number>10</Number>
</Values>
<Values>
<Value>2</Value>
<Number>100</Number>
</Values>
<Values>
<Value>3</Value>
<Number>1000</Number>
</Values>
你可以做到
<xsl:variable name="tokens" select="exsl:node-set($tokens-rtf)/Values" xmlns:exsl="http://exslt.org/common"/>
现在你有一个节点集可以应用任何XPath,就像你想要做的位置谓词一样。 <xsl:variable name"Foo" select="$tokens[$n]"/>
。
答案 3 :(得分:0)
此解决方案是迄今为止唯一没有使用扩展功能的解决方案,并且不会假设数字将以任何预定义的顺序检索:
<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="Value">
<Values>
<xsl:copy-of select="."/>
<Number>
<xsl:apply-templates select="/*/Numbers" mode="call">
<xsl:with-param name="pPos" select="."/>
</xsl:apply-templates>
</Number>
</Values>
</xsl:template>
<xsl:template match="Numbers" name="getNth" mode="call">
<xsl:param name="pNums" select="concat(., ',')"/>
<xsl:param name="pPos"/>
<xsl:param name="pResult"/>
<xsl:if test="not($pPos > 0)">
<xsl:value-of select="$pResult"/>
</xsl:if>
<xsl:if test="$pNums and $pPos > 0">
<xsl:call-template name="getNth">
<xsl:with-param name="pNums" select="substring-after($pNums, ',')"/>
<xsl:with-param name="pResult" select="substring-before($pNums, ',')"/>
<xsl:with-param name="pPos" select="$pPos -1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="/*">
<xsl:copy>
<Results><xsl:apply-templates/></Results>
</xsl:copy>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
在提供的XML文档上应用此转换时:
<Table>
<Numbers>10,100,1000</Numbers>
<Values>
<Value>1</Value>
<Value>2</Value>
<Value>3</Value>
</Values>
</Table>
产生了想要的正确结果:
<Table>
<Results>
<Values>
<Value>1</Value>
<Number>10</Number>
</Values>
<Values>
<Value>2</Value>
<Number>100</Number>
</Values>
<Values>
<Value>3</Value>
<Number>1000</Number>
</Values>
</Results>
</Table>
对此XML文档应用相同的转换时(请注意更改的值顺序):
<Table>
<Numbers>10,100,1000</Numbers>
<Values>
<Value>3</Value>
<Value>1</Value>
<Value>2</Value>
</Values>
</Table>
再次正确生成此XML文档的结果:
<Table>
<Results>
<Values>
<Value>3</Value>
<Number>1000</Number>
</Values>
<Values>
<Value>1</Value>
<Number>10</Number>
</Values>
<Values>
<Value>2</Value>
<Number>100</Number>
</Values>
</Results>
</Table>