我需要一个转换来查找节点中文本模式的索引。例如,在下面的XML中。如果<txt>
节点的文本模式为“ain”,则答案为6,15,26和41。
<root>
<info find="ain">
<txt>The rain in Spain falls mainly in the plain.</txt>
</info>
</root>
转换为......
<find>
<txt>The rain in Spain falls mainly in the plain.</txt>
<hit ndx="6"/>
<hit ndx="15"/>
<hit ndx="26"/>
<hit ndx="41"/>
</find>
答案 0 :(得分:3)
编辑:这是一个XSLT 2.0解决方案:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*" />
<xsl:template match="info[@find]">
<find>
<xsl:copy-of select="txt[1]" />
<xsl:variable name="pattern"
select="replace(@find, '[-/\\^$*+?.()|\[\]{}]', '\\$0')" />
<xsl:variable name="parts" select="tokenize(txt, $pattern)" />
<xsl:for-each select="1 to count($parts) - 1">
<xsl:variable name="soFar"
select="string-join($parts[position() <= current()],
$pattern)" />
<hit ndx="{1 + string-length($soFar)}" />
</xsl:for-each>
</find>
</xsl:template>
</xsl:stylesheet>
因为我已经做过这个,所以这是一个XSLT 1.0方法。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*" />
<xsl:template match="info[@find]">
<find>
<xsl:copy-of select="txt[1]" />
<xsl:call-template name="Matches">
<xsl:with-param name="text" select="txt[1]" />
<xsl:with-param name="pattern" select="@find" />
</xsl:call-template>
</find>
</xsl:template>
<xsl:template name="Matches">
<xsl:param name="text" />
<xsl:param name="pattern" />
<xsl:param name="offset" select="1" />
<xsl:variable name="found" select="substring-before($text, $pattern)" />
<xsl:if test="$found">
<hit ndx="{$offset + string-length($found)}" />
<xsl:call-template name="Matches">
<xsl:with-param name="text" select="substring-after($text, $pattern)" />
<xsl:with-param name="pattern" select="$pattern" />
<xsl:with-param name="offset"
select="$offset +
string-length($found) +
string-length($pattern)" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
当您对样本输入运行任一项时,结果为:
<find>
<txt>The rain in Spain falls mainly in the plain.</txt>
<hit ndx="6" />
<hit ndx="15" />
<hit ndx="26" />
<hit ndx="41" />
</find>
答案 1 :(得分:3)
此XSLT 2.0转换:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/*/info">
<xsl:variable name="vSeq" select="string-to-codepoints(txt)"/>
<xsl:variable name="vPatSeq" select="string-to-codepoints(@find)"/>
<xsl:sequence select=
"for $vPat in string(@find),
$vPatLength in string-length(@find)
return
index-of($vSeq, $vPatSeq[1])
[$vPat eq codepoints-to-string(subsequence($vSeq, ., $vPatLength))]
"/>
</xsl:template>
</xsl:stylesheet>
应用于提供的XML文档时:
<root>
<info find="ain">
<txt>The rain in Spain falls mainly in the plain.</txt>
</info>
</root>
生成正确的结果:
6 15 26 41
以下是使用它来生成所需XML结果的同样短的转换:
<xsl:stylesheet version="2.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="/*/info">
<xsl:variable name="vSeq" select="string-to-codepoints(txt)"/>
<xsl:variable name="vPatSeq" select="string-to-codepoints(@find)"/>
<find>
<xsl:copy-of select="txt"/>
<xsl:for-each select=
"for $vPat in string(@find),
$vPatLength in string-length(@find)
return
index-of($vSeq, $vPatSeq[1])
[$vPat eq codepoints-to-string(subsequence($vSeq, ., $vPatLength))]">
<hit ndx="{.}"/>
</xsl:for-each>
</find>
</xsl:template>
</xsl:stylesheet>
当此转换应用于同一个提供的XML文档(上图)时,生成所需结果:
<find>
<txt>The rain in Spain falls mainly in the plain.</txt>
<hit ndx="6"/>
<hit ndx="15"/>
<hit ndx="26"/>
<hit ndx="41"/>
</find>
或者,可以使用:
<xsl:for-each select=
"(1 to string-length(txt) -string-length($vPat) +1)
[starts-with(substring($vTxt, .), $vPat)]
">
完整转型:
<xsl:stylesheet version="2.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="/*/info">
<xsl:variable name="vTxt" select="txt"/>
<xsl:variable name="vPat" select="string(@find)"/>
<find>
<xsl:copy-of select="txt"/>
<xsl:for-each select=
"(1 to string-length(txt) -string-length($vPat) +1)
[starts-with(substring($vTxt, .), $vPat)]
">
<hit ndx="{.}"/>
</xsl:for-each>
</find>
</xsl:template>
</xsl:stylesheet>
请注意此解决方案的简单性和直接性:
没有递归。
没有命名模板。
否xsl:function
s。
否xsl:param
s。
否xsl:if
。
没有其他名称空间声明。
否substring-after()
。
没有RegExes。
否replace()
。
否tokenize()
。
没有regex-group()`s。
否string-join()
。
否count()
。
答案 2 :(得分:1)
使用递归。如果您使用的是XSLT2,那么创建函数最简单:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:s="http://string-functions"
version="2.0">
<xsl:function name="s:indexes" as="element(find)">
<xsl:param name="str1"/>
<xsl:param name="str2"/>
<find value="{$str2}">
<txt><xsl:value-of select="$str1"/></txt>
<xsl:sequence select="s:indexes($str1, $str2, 0)"/>
</find>
</xsl:function>
<xsl:function name="s:indexes" as="element(hit)*">
<xsl:param name="str1"/>
<xsl:param name="str2"/>
<xsl:param name="offset"/>
<xsl:variable name="sub-before" select="substring-before($str1, $str2)"/>
<xsl:if test="$sub-before ne ''">
<xsl:variable name="position" select="$offset + string-length($sub-before) + 1"/>
<xsl:variable name="rest" select="substring(substring-after($str1, $sub-before), string-length($str2))"/>
<xsl:variable name="new-offset" select="$offset + string-length($str1) - string-length($rest)"/>
<hit test="{$position}"/>
<xsl:sequence select="s:indexes($rest, $str2, $new-offset)"/>
</xsl:if>
</xsl:function>
<xsl:template match="*">
<xsl:sequence select="s:indexes('The rain in Spain falls mainly in the plain', 'ain')"/>
</xsl:template>
</xsl:stylesheet>
=&GT;
<find value="ain">
<txt>The rain in Spain falls mainly in the plain</txt>
<hit test="6"/>
<hit test="15"/>
<hit test="26"/>
<hit test="41"/>
</find>