XSLT - 多次替换字符串
XSLT - replace a String multiple times
我有一个像这样的 xml,(<p>/text()
是变化的)
<doc>
<p> solid 1; thick 2; solid 2;</p>
<p> double 2; thick 2; dotted 1;</p>
<p> dotted 1; double 2; dotted 2;</p>
<p> solid 2; thick 2; dotted 2;</p>
</doc>
我的要求是分析 <p>
节点文本并替换以下字符串,
solid 1; to solid 2;
solid 2; to solid 4;
dotted 1; to dotted 2;
dotted 2; to dotted 4;
所以,预期的输出应该是这样的,
<doc>
<p> solid 2; thick 2; solid 4;</p>
<p> double 2; thick 2; dotted 2;</p>
<p> dotted 2; double 2; dotted 4;</p>
<p> solid 4; thick 2; dotted 4;</p>
</doc>
我写了下面的 xslt 来完成这个任务,
<xsl:template name='replace-text'>
<xsl:param name='text'/>
<xsl:param name='replace'/>
<xsl:param name='by'/>
<xsl:choose>
<xsl:when test='contains($text, $replace)'>
<xsl:value-of select='substring-before($text, $replace)'/>
<xsl:value-of select='$by' disable-output-escaping='yes'/>
<xsl:call-template name='replace-text'>
<xsl:with-param name='text' select='substring-after($text, $replace)'/>
<xsl:with-param name='replace' select='$replace'/>
<xsl:with-param name='by' select='$by'/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select='$text'/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="p/text()">
<xsl:call-template name="replace-text">
<xsl:with-param name="text" select="."/>
<xsl:with-param name="replace" select="'solid 1'"/>
<xsl:with-param name="by" select="'solid 2'"/>
</xsl:call-template>
</xsl:template>
但是这里我一次只能传递一个参数。我正在努力针对这种情况在派系编程中实施一种方法,任何人都可以建议我一种方法来完成这项任务吗?
只需使用 replace() 函数和一个 for-each 循环。
<doc>
<xsl:for-each select="p">
<p>
<xsl:value-of select=" fn:replace(
fn:replace(
fn:replace(
fn:replace(.,'solid 2','solid 4'),
'solid 1','solid 2'),
'dotted 2','dotted 4'),
'dotted 1','dotted 2')"/>
</p>
</xsl:for-each>
</doc>
在这种情况下,您必须注意先替换 "solid 2",然后再替换 "solid 1"。如果您首先将 "solid 1" 替换为 "solid 2",它将再次被 "solid 4" 替换,因为顺序。最里面的函数将首先应用于字符串。
更灵活的解决方案,不使用 N 次嵌套 replace()
调用:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p/text()">
<xsl:for-each select="tokenize(., ';')">
<xsl:analyze-string select="." regex="((solid)|(dotted)) (\d)">
<xsl:matching-substring>
<xsl:value-of select=
"concat(regex-group(1), ' ', 2*xs:integer(regex-group(4)), ';')"/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:sequence select="."></xsl:sequence>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
一个更通用的解决方案,其中标记的替换在参数中指定(并且可以在单独的XML文档中指定):
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pMapping">
<mapArgOf name="solid" old="1" new="2"/>
<mapArgOf name="solid" old="2" new="4"/>
<mapArgOf name="dotted" old="1" new="2"/>
<mapArgOf name="dotted" old="2" new="4"/>
</xsl:param>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p/text()">
<xsl:for-each select="tokenize(., ';')[.]">
<xsl:variable name="vTokens" select="tokenize(., '\s+')[.]"/>
<xsl:variable name="vMatch"
select="$pMapping/*[@name eq $vTokens[1] and @old eq $vTokens[2]]"/>
<xsl:value-of select=
"concat(replace(., $vTokens[2], ($vMatch/@new, $vTokens[2])[1]), ';')"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
这两个转换,当应用于提供的 XML 文档时:
<doc>
<p> solid 1; thick 2; solid 2;</p>
<p> double 2; thick 2; dotted 1;</p>
<p> dotted 1; double 2; dotted 2;</p>
<p> solid 2; thick 2; dotted 2;</p>
</doc>
产生想要的正确结果:
<doc>
<p> solid 2; thick 2; solid 4;</p>
<p> double 2; thick 2; dotted 2;</p>
<p> dotted 2; double 2; dotted 4;</p>
<p> solid 4; thick 2; dotted 4;</p>
</doc>
注意:
我们可以根据需要指定任意数量的映射(不仅限于 "solid" 和 "dotted")。
这里我们不再假设新值是旧值的两倍——我们甚至不假设该值是一个数字
例如,如果我们要为 "thick" 添加替换,例如 1 被 5 替换,2 被 8 替换,3 被 10 替换,我们只需像这样更改映射:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pMapping">
<mapArgOf name="solid" old="1" new="2"/>
<mapArgOf name="solid" old="2" new="4"/>
<mapArgOf name="dotted" old="1" new="2"/>
<mapArgOf name="dotted" old="2" new="4"/>
<mapArgOf name="thick" old="1" new="5"/>
<mapArgOf name="thick" old="2" new="8"/>
<mapArgOf name="thick" old="3" new="10"/>
</xsl:param>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p/text()">
<xsl:for-each select="tokenize(., ';')[.]">
<xsl:variable name="vTokens" select="tokenize(., '\s+')[.]"/>
<xsl:variable name="vMatch" select=
"$pMapping/*[@name eq $vTokens[1] and @old eq $vTokens[2]]"/>
<xsl:value-of select=
"concat(replace(., $vTokens[2], ($vMatch/@new, $vTokens[2])[1]), ';')"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
现在我们又得到了新的想要的结果:
<doc>
<p> solid 2; thick 5; solid 4;</p>
<p> double 2; thick 8; dotted 2;</p>
<p> dotted 2; double 2; dotted 4;</p>
<p> solid 4; thick 10; dotted 4;</p>
</doc>
最后,对于最通用的 multi-replace 解决方案,请参阅此答案:
XSL Multiple search and replace function
我有一个像这样的 xml,(<p>/text()
是变化的)
<doc>
<p> solid 1; thick 2; solid 2;</p>
<p> double 2; thick 2; dotted 1;</p>
<p> dotted 1; double 2; dotted 2;</p>
<p> solid 2; thick 2; dotted 2;</p>
</doc>
我的要求是分析 <p>
节点文本并替换以下字符串,
solid 1; to solid 2;
solid 2; to solid 4;
dotted 1; to dotted 2;
dotted 2; to dotted 4;
所以,预期的输出应该是这样的,
<doc>
<p> solid 2; thick 2; solid 4;</p>
<p> double 2; thick 2; dotted 2;</p>
<p> dotted 2; double 2; dotted 4;</p>
<p> solid 4; thick 2; dotted 4;</p>
</doc>
我写了下面的 xslt 来完成这个任务,
<xsl:template name='replace-text'>
<xsl:param name='text'/>
<xsl:param name='replace'/>
<xsl:param name='by'/>
<xsl:choose>
<xsl:when test='contains($text, $replace)'>
<xsl:value-of select='substring-before($text, $replace)'/>
<xsl:value-of select='$by' disable-output-escaping='yes'/>
<xsl:call-template name='replace-text'>
<xsl:with-param name='text' select='substring-after($text, $replace)'/>
<xsl:with-param name='replace' select='$replace'/>
<xsl:with-param name='by' select='$by'/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select='$text'/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="p/text()">
<xsl:call-template name="replace-text">
<xsl:with-param name="text" select="."/>
<xsl:with-param name="replace" select="'solid 1'"/>
<xsl:with-param name="by" select="'solid 2'"/>
</xsl:call-template>
</xsl:template>
但是这里我一次只能传递一个参数。我正在努力针对这种情况在派系编程中实施一种方法,任何人都可以建议我一种方法来完成这项任务吗?
只需使用 replace() 函数和一个 for-each 循环。
<doc>
<xsl:for-each select="p">
<p>
<xsl:value-of select=" fn:replace(
fn:replace(
fn:replace(
fn:replace(.,'solid 2','solid 4'),
'solid 1','solid 2'),
'dotted 2','dotted 4'),
'dotted 1','dotted 2')"/>
</p>
</xsl:for-each>
</doc>
在这种情况下,您必须注意先替换 "solid 2",然后再替换 "solid 1"。如果您首先将 "solid 1" 替换为 "solid 2",它将再次被 "solid 4" 替换,因为顺序。最里面的函数将首先应用于字符串。
更灵活的解决方案,不使用 N 次嵌套 replace()
调用:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p/text()">
<xsl:for-each select="tokenize(., ';')">
<xsl:analyze-string select="." regex="((solid)|(dotted)) (\d)">
<xsl:matching-substring>
<xsl:value-of select=
"concat(regex-group(1), ' ', 2*xs:integer(regex-group(4)), ';')"/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:sequence select="."></xsl:sequence>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
一个更通用的解决方案,其中标记的替换在参数中指定(并且可以在单独的XML文档中指定):
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pMapping">
<mapArgOf name="solid" old="1" new="2"/>
<mapArgOf name="solid" old="2" new="4"/>
<mapArgOf name="dotted" old="1" new="2"/>
<mapArgOf name="dotted" old="2" new="4"/>
</xsl:param>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p/text()">
<xsl:for-each select="tokenize(., ';')[.]">
<xsl:variable name="vTokens" select="tokenize(., '\s+')[.]"/>
<xsl:variable name="vMatch"
select="$pMapping/*[@name eq $vTokens[1] and @old eq $vTokens[2]]"/>
<xsl:value-of select=
"concat(replace(., $vTokens[2], ($vMatch/@new, $vTokens[2])[1]), ';')"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
这两个转换,当应用于提供的 XML 文档时:
<doc>
<p> solid 1; thick 2; solid 2;</p>
<p> double 2; thick 2; dotted 1;</p>
<p> dotted 1; double 2; dotted 2;</p>
<p> solid 2; thick 2; dotted 2;</p>
</doc>
产生想要的正确结果:
<doc>
<p> solid 2; thick 2; solid 4;</p>
<p> double 2; thick 2; dotted 2;</p>
<p> dotted 2; double 2; dotted 4;</p>
<p> solid 4; thick 2; dotted 4;</p>
</doc>
注意:
我们可以根据需要指定任意数量的映射(不仅限于 "solid" 和 "dotted")。
这里我们不再假设新值是旧值的两倍——我们甚至不假设该值是一个数字
例如,如果我们要为 "thick" 添加替换,例如 1 被 5 替换,2 被 8 替换,3 被 10 替换,我们只需像这样更改映射:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pMapping">
<mapArgOf name="solid" old="1" new="2"/>
<mapArgOf name="solid" old="2" new="4"/>
<mapArgOf name="dotted" old="1" new="2"/>
<mapArgOf name="dotted" old="2" new="4"/>
<mapArgOf name="thick" old="1" new="5"/>
<mapArgOf name="thick" old="2" new="8"/>
<mapArgOf name="thick" old="3" new="10"/>
</xsl:param>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p/text()">
<xsl:for-each select="tokenize(., ';')[.]">
<xsl:variable name="vTokens" select="tokenize(., '\s+')[.]"/>
<xsl:variable name="vMatch" select=
"$pMapping/*[@name eq $vTokens[1] and @old eq $vTokens[2]]"/>
<xsl:value-of select=
"concat(replace(., $vTokens[2], ($vMatch/@new, $vTokens[2])[1]), ';')"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
现在我们又得到了新的想要的结果:
<doc>
<p> solid 2; thick 5; solid 4;</p>
<p> double 2; thick 8; dotted 2;</p>
<p> dotted 2; double 2; dotted 4;</p>
<p> solid 4; thick 10; dotted 4;</p>
</doc>
最后,对于最通用的 multi-replace 解决方案,请参阅此答案:
XSL Multiple search and replace function