说我有这样的XML:
<parole>
<parola id="a">1</parola>
<parola id="b">2</parola>
<parola id="c">3</parola>
<parola id="a">4</parola>
<parola id="a">5</parola>
<parola id="b">6</parola>
</parole>
现在,我知道generate-id()
函数存在。但是,出于学习目的,我想知道如何使用XSLT更改名为&#34; id&#34;的属性值。
我已经考虑过&#34;算法&#34;喜欢:
考虑属性的以下和前一个兄弟 如果您遇到当前属性的副本,请添加&#34; f&#34;到这个名字的末尾和(递归地)添加另一个&#34; f&#34;直到我们没有具有相同值的属性。
所以我的最终XML就像:
<parole>
<parola id="a">1</parola>
<parola id="b">2</parola>
<parola id="c">3</parola>
<parola id="af">4</parola>
<parola id="aff">5</parola>
<parola id="bf">6</parola>
</parole>
现在,我尝试使用递归函数获得此结果,如:
<xsl:variable name="following-siblings-ids" select="/parole/parola/following-sibling::parola/@id"/>
<xsl:variable name="preceding-siblings-ids" select="/parole/parola/preceding-sibling::parola/@id"/>
<xsl:function name="du:check" as="xs:string">
<xsl:param name="id" />
<xsl:choose>
<xsl:when test="$id = $following-siblings-ids and $preceding-siblings-ids">
<xsl:value-of select="du:check(concat($id, 'f'))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$id"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<xsl:template match="parola">
<xsl:value-of select="du:check(@id)"/>
</xsl:template match="parola">
但我得到了像
这样的结果&#34; AF&#34; &#34; BF&#34; &#34; CF&#34; &#34; AF&#34; &#34; AF&#34; &#34; BF&#34;
而不是所需的那个。
任何提示?在使用动态变量和for-each
的非声明性语言中,这将是一项简单的任务,但我不知道如何在XSLT中实现它...
答案 0 :(得分:1)
这是一个XSLT 1解决方案。以下转型
<xsl:stylesheet
version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- standard copy template -->
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*" />
</xsl:copy>
</xsl:template>
<xsl:template name="make-f">
<xsl:param name="n"/>
<xsl:if test="$n > 0">f<xsl:call-template name="make-f">
<xsl:with-param name="n" select="$n - 1"/></xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="parola/@id">
<xsl:variable name="id" select="."/>
<xsl:variable name="f">
<xsl:call-template name="make-f">
<xsl:with-param name="n" select="count(../preceding-sibling::parola/@id[.=$id])"/>
</xsl:call-template>
</xsl:variable>
<xsl:attribute name="id">
<xsl:value-of select="concat(., $f)"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
应用于输入样本时,会产生以下输出:
$ xsltproc test.xsl test.xml
<?xml version="1.0"?>
<parole>
<parola id="a">1</parola>
<parola id="b">2</parola>
<parola id="c">3</parola>
<parola id="af">4</parola>
<parola id="aff">5</parola>
<parola id="bf">6</parola>
</parole>
答案 1 :(得分:0)
我会使用一个键通过id属性“分组”元素,然后识别该组中的位置以附加索引,或者如果您想要f
个字母的数量:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs mf"
version="2.0">
<xsl:param name="n" select="'f'"/>
<xsl:function name="mf:node-index-of" as="xs:integer">
<xsl:param name="node" as="node()"/>
<xsl:param name="sequence" as="node()*"/>
<xsl:sequence select="for $pos in 1 to count($sequence) return $pos[$node is $sequence[$pos]]"/>
</xsl:function>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:key name="p-by-id" match="parola" use="@id"/>
<xsl:template match="parola[not(. is key('p-by-id', @id)[1])]/@id">
<xsl:attribute name="id" select="concat(., string-join(for $i in 1 to mf:node-index-of(.., key('p-by-id', .)) - 1 return $n, ''))"/>
</xsl:template>
</xsl:stylesheet>
带有match="@* | node()"
的模板是所谓的身份转换模板,它可以逐层复制所有未更改的内容,允许我们为要转换的节点添加更多模板。
您要转换的唯一节点是id
元素的parola
属性,其中存在多个具有特定ID值的元素,因此我为此编写了一个模板match="parola[not(. is key('p-by-id', @id)[1])]/@id"
,使用键<xsl:key name="p-by-id" match="parola" use="@id"/>
来标识具有相同值的所有元素。
然后通过将现有值与包含concat(., string-join(for $i in 1 to mf:node-index-of(.., key('p-by-id', .)) - 1 return $n, ''))
的序列连接起来,计算新属性值$n
,该序列比其父parola
的位置索引的次数少一个组。
如果您不熟悉XSLT和XPath 2.0,请花些时间阅读有关身份转换,密钥和用户定义函数的教程或书籍。