英文:
Predicates vs recursive templates vs other
问题
以下是翻译的内容:
考虑以下简单问题:
我们希望将此输入映射到相同的输出,除了第一个出现的具有“@bar = '1'”的“foo”元素,我们添加一个新属性@wibble,因此,原始输入:
<root>
<foo/>
<foo/>
<foo/>
<foo bar="1"/>
<foo bar="1"/>
<foo/>
<foo/>
<foo/>
<foo/>
<foo/>
</root>
将变为:
<root>
<foo />
<foo />
<foo />
<foo wibble="2" bar="1" />
<foo bar="1" />
<foo />
<foo />
<foo />
<foo />
<foo />
</root>
我可以使用身份模式来实现此映射(不确定此模式的名称),但实现如下:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates select="root" mode="findFirst"/>
</xsl:template>
<xsl:template match="@* | node()" mode="findFirst">
<xsl:copy>
<xsl:apply-templates select="@* | node()" mode="findFirst"/>
</xsl:copy>
</xsl:template>
<xsl:template match="foo[@bar='1'][1]" mode="findFirst">
<xsl:copy>
<xsl:attribute name="wibble">2</xsl:attribute>
<xsl:apply-templates select="@* | node()" mode="findFirst"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
也就是说,我们使用一些匹配语句覆盖了身份模板,以匹配我们要匹配的特定情况,实施我们的覆盖映射,然后继续。
我经常使用这种风格。
然而,有时匹配语句很复杂(我们最近在另一个关于代码行映射的问题中看到了这一点)。我发现这些匹配有问题,因为在上述情况下用例很简单,但有时逻辑不容易(或根本不能)在匹配语句内表达,这种情况下,我倾向于使用递归函数模式,在这种情况下,我会编写一个类似于以下的递归模板。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<root>
<xsl:apply-templates select="root/foo[1]" mode="findFirst">
<xsl:with-param name="isFound" select="false()"/>
</xsl:apply-templates>
</root>
</xsl:template>
<xsl:template match="foo" mode="findFirst">
<xsl:param name="isFound"/>
<xsl:copy>
<xsl:if test="$isFound = false() and @bar = '1'">
<xsl:attribute name="wibble">2</xsl:attribute>
</xsl:if>
<xsl:apply-templates select="@* | node()" mode="identity"/>
</xsl:copy>
<xsl:choose>
<xsl:when test="$isFound = false() and @bar = '1'">
<xsl:apply-templates select="following-sibling::foo[1]" mode="findFirst">
<xsl:with-param name="isFound" select="true()"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="following-sibling::foo[1]" mode="findFirst">
<xsl:with-param name="isFound" select="$isFound"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="@* | node()" mode="identity">
<xsl:copy>
<xsl:apply-templates select="@* | node()" mode="identity"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
这基本上将节点集视为功能性的“列表”,获取头部(并隐式传递尾部)。
现在我们可以实现更复杂的逻辑并使用参数通过递归传递(实际上是折叠)的当前状态,但代价
英文:
consider this simple problem:
we wish to map this input to the same output except the first occurence of a 'foo' element with "@bar = '1'", we add a new attribute @wibble, so this:
<root>
<foo/>
<foo/>
<foo/>
<foo bar="1"/>
<foo bar="1"/>
<foo/>
<foo/>
<foo/>
<foo/>
<foo/>
</root>
goes to this:
<root>
<foo />
<foo />
<foo />
<foo wibble="2" bar="1" />
<foo bar="1" />
<foo />
<foo />
<foo />
<foo />
<foo />
</root>
I could implement this mapping using the identity pattern (not sure what this pattern is called), but it would go like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates select="root" mode="findFirst"/>
</xsl:template>
<xsl:template match="@* | node()" mode="findFirst">
<xsl:copy>
<xsl:apply-templates select="@* | node()" mode="findFirst"/>
</xsl:copy>
</xsl:template>
<xsl:template match="foo[@bar='1'][1]" mode="findFirst">
<xsl:copy>
<xsl:attribute name="wibble">2</xsl:attribute>
<xsl:apply-templates select="@* | node()" mode="findFirst"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
i.e. we override the identity template with some match statement which matches the specific scenario we want to match, implement our overriding mapping, and then continue.
I use this style a lot.
Sometimes though the match statement is complex (we saw this in another question recently about mapping lines of code). I find these sort of matches problematic, in the above scenario the use case is simple, but sometimes the logic isnt easily (or at all) expressibly inside the match statement, in which case I'm tempted to fall back on recursive functional patterns, and in this case I'd write a recursive template like this.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<root>
<xsl:apply-templates select="root/foo[1]" mode="findFirst">
<xsl:with-param name="isFound" select="false()"/>
</xsl:apply-templates>
</root>
</xsl:template>
<xsl:template match="foo" mode="findFirst">
<xsl:param name="isFound"/>
<xsl:copy>
<xsl:if test="$isFound = false() and @bar = '1'">
<xsl:attribute name="wibble">2</xsl:attribute>
</xsl:if>
<xsl:apply-templates select="@* | node()" mode="identity"/>
</xsl:copy>
<xsl:choose>
<xsl:when test="$isFound = false() and @bar = '1'">
<xsl:apply-templates select="following-sibling::foo[1]" mode="findFirst">
<xsl:with-param name="isFound" select="true()"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="following-sibling::foo[1]" mode="findFirst">
<xsl:with-param name="isFound" select="$isFound"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="@* | node()" mode="identity">
<xsl:copy>
<xsl:apply-templates select="@* | node()" mode="identity"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
this basically treats the nodeset as a functional 'list', taking the head (and passing the tail implicitly).
Now we can implement much more complex logic and use parameters to pass the current state of the (effectively fold) through the recursion, but at the cost of extra complexity.
BUT....
-
Is this style of programming sustainable in XSLT? - I always worry about stack overflow (ironically!), due to probable non tail recursion in the XSLT engine of the recursive template.
-
My knowledge of XSLT 3.0 is extremely limited (any references to good learning resources always appreciated), but in a FP language the alternative to direct recursion would be to use fold, where fold is written as a tail recursive function, and fold IS available in XSLT 3.0, but is this a sensible alternative?
-
are there other patterns of usage that I can use?
答案1
得分: 2
XSLT 中有 xsl:iterate
(https://www.w3.org/TR/xslt-30/#iterate)允许您以一种声明性的方式实现兄弟递归,看起来有点像循环,并由于其结构和实现避免了任何堆栈溢出递归; [迭代示例][1]:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:iterate select="node()">
<xsl:param name="found" select="false()"/>
<xsl:variable name="is-first-foo" select="if (. instance of element(foo)) then not($found) and boolean(self::foo[@bar = 1]) else $found"/>
<xsl:choose>
<xsl:when test="$is-first-foo">
<xsl:copy>
<xsl:attribute name="wibble" select="2"/>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="."/>
</xsl:otherwise>
</xsl:choose>
<xsl:next-iteration>
<xsl:with-param name="found" select="$is-first-foo"/>
</xsl:next-iteration>
</xsl:iterate>
</xsl:copy>
</xsl:template>
<xsl:mode on-no-match="shallow-copy"/>
</xsl:stylesheet>
fold-left
在 XPath 3.1 级别当然也是可用的,将其与 XSLT(3.0)的 XML 语法集成要比在 XQuery 3.1 中复杂一些,因为基本上一切都是表达式。但这当然是一个选项; [在线示例][2]:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
xmlns:mf="http://example.com/mf"
expand-text="yes">
<xsl:function name="mf:add-attribute" as="element()">
<xsl:param name="element" as="element()"/>
<xsl:copy select="$element">
<xsl:attribute name="wibble" select="2"/>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:function>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:sequence
select="fold-left(
node(),
map { 'found-foos' : 0, 'nodes' : () },
function($a, $n) {
let $is-foo := $n instance of element(foo) and boolean($n/self::foo[@bar = 1]),
$is-first-foo := $a?found-foos = 0 and $is-foo
return
map {
'found-foos' : if ($is-foo) then $a?found-foos + 1 else $a?found-foos,
'nodes': ($a?nodes, if ($is-first-foo) then mf:add-attribute($n) else $n)
}
})?nodes"/>
</xsl:copy>
</xsl:template>
<xsl:mode on-no-match="shallow-copy"/>
</xsl:stylesheet>
对于您的示例,累加器可能允许您以声明性方式检查条件,然后在匹配模式中使用其值来检查是否需要添加属性。[在线累加器使用示例][3]:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
expand-text="yes">
<xsl:param name="pattern" static="yes" as="xs:string" select="'
<details>
<summary>英文:</summary>
XSLT has `xsl:iterate` (https://www.w3.org/TR/xslt-30/#iterate) which allows you to implement your sibling recursion in a declarative way that looks a bit like a loop and due to its structure and implementation avoids any stack overflow recursion; [iterate example][1]:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:iterate select="node()">
<xsl:param name="found" select="false()"/>
<xsl:variable name="is-first-foo" select="if (. instance of element(foo)) then not($found) and boolean(self::foo[@bar = 1]) else $found"/>
<xsl:choose>
<xsl:when test="$is-first-foo">
<xsl:copy>
<xsl:attribute name="wibble" select="2"/>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="."/>
</xsl:otherwise>
</xsl:choose>
<xsl:next-iteration>
<xsl:with-param name="found" select="$is-first-foo"/>
</xsl:next-iteration>
</xsl:iterate>
</xsl:copy>
</xsl:template>
<xsl:mode on-no-match="shallow-copy"/>
</xsl:stylesheet>
`fold-left` is certainly also available at the XPath 3.1 level, integrating it with the XML syntax of XSLT (3.0) is a bit more convoluted than in XQuery 3.1 where basically all is an expression. But is is certainly an option; [example online][2]:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
xmlns:mf="http://example.com/mf"
expand-text="yes">
<xsl:function name="mf:add-attribute" as="element()">
<xsl:param name="element" as="element()"/>
<xsl:copy select="$element">
<xsl:attribute name="wibble" select="2"/>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:function>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:sequence
select="fold-left(
node(),
map { 'found-foos' : 0, 'nodes' : () },
function($a, $n) {
let $is-foo := $n instance of element(foo) and boolean($n/self::foo[@bar = 1]),
$is-first-foo := $a?found-foos = 0 and $is-foo
return
map {
'found-foos' : if ($is-foo) then $a?found-foos + 1 else $a?found-foos,
'nodes': ($a?nodes, if ($is-first-foo) then mf:add-attribute($n) else $n)
}
}
)?nodes"/>
</xsl:copy>
</xsl:template>
<xsl:mode on-no-match="shallow-copy"/>
</xsl:stylesheet>
And for your sample an accumulator might allow you to check your conditions in a declarative way and then use its value in your match pattern to check whether you need to add your attribute. [Online sample of accumulator use][3]:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:param name="pattern" static="yes" as="xs:string" select="'foo[@bar = 1][1]'"/>
<xsl:accumulator name="have-first-foo-bar" as="xs:boolean" initial-value="false()">
<xsl:accumulator-rule _match="{$pattern}" select="true()"/>
<xsl:accumulator-rule phase="end" _match="{$pattern}" select="false()"/>
</xsl:accumulator>
<xsl:template match="foo[accumulator-before('have-first-foo-bar')]">
<xsl:copy>
<xsl:attribute name="wibble" select="2"/>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:mode on-no-match="shallow-copy" use-accumulators="#all"/>
</xsl:stylesheet>
[1]: https://martin-honnen.github.io/xslt3fiddle/?xslt=%3Cxsl%3Astylesheet%20xmlns%3Axsl%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2FXSL%2FTransform%22%0D%0A%20%20version%3D%223.0%22%0D%0A%20%20xmlns%3Axs%3D%22http%3A%2F%2Fwww.w3.org%2F2001%2FXMLSchema%22%0D%0A%20%20exclude-result-prefixes%3D%22%23all%22%0D%0A%20%20expand-text%3D%22yes%22%3E%0D%0A%20%20%0D%0A%20%20%3Cxsl%3Atemplate%20match%3D%22%2F*%22%3E%0D%0A%20%20%20%20%3Cxsl%3Acopy%3E%0D%0A%20%20%20%20%20%20%3Cxsl%3Aapply-templates%20select%3D%22%40*%22%2F%3E%0D%0A%20%20%20%20%20%20%3Cxsl%3Aiterate%20select%3D%22node%28%29%22%3E%0D%0A%20%20%20%20%20%20%20%20%3Cxsl%3Aparam%20name%3D%22found%22%20select%3D%22false%28%29%22%2F%3E%0D%0A%20%20%20%20%20%20%20%20%3Cxsl%3Avariable%20name%3D%22is-first-foo%22%20select%3D%22if%20%28.%20instance%20of%20element%28foo%29%29%20then%20not%28%24found%29%20and%20boolean%28self%3A%3Afoo%5B%40bar%20%3D%201%5D%29%20else%20%24found%22%2F%3E%0D%0A%20%20%20%20%20%20%20%20%3Cxsl%3Achoose%3E%0D%0A%20%20%20%20%20%20%20%20%20%20%3Cxsl%3Awhen%20test%3D%22%24is-first-foo%22%3E%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Cxsl%3Acopy%3E%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cxsl%3Aattribute%20name%3D%22wibble%22%20select%3D%222%22%2F%3E%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cxsl%3Aapply-templates%20select%3D%22%40*%22%2F%3E%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cxsl%3Aapply-templates%2F%3E%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%3C%2Fxsl%3Acopy%3E%0D%0A%20%20%20%20%20%20%20%20%20%20%3C%2Fxsl%3Awhen%3E%0D%0A%20%20%20%20%20%20%20%20%20%20%3Cxsl%3Aotherwise%3E%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Cxsl%3Aapply-templates%20select%3D%22.%22%2F%3E%0D%0A%20%20%20%20%20%20%20%20%20%20%3C%2Fxsl%3Aotherwise%3E%0D%0A%20%20%20%20%20%20%20%20%3C%2Fxsl%3Achoose%3E%0D%0A%20%20%20%20%20%20%20%20%3Cxsl%3Anext-iteration%3E%0D%0A%20%20%20%20%20%20%20%20%20%20%3Cxsl%3Awith-param%20name%3D%22found%22%20select%3D%22%24is-first-foo%22%2F%3E%0D%0A%20%20%20%20%20%20%20%20%3C%2Fxsl%3Anext-iteration%3E%0D%0A%20%20%20%20%20%20%3C%2Fxsl%3Aiterate%3E%0D%0A%20%20%20%20%3C%2Fxsl%3Acopy%3E%0D%0A%20%20%3C%2Fxsl%3Atemplate%3E%0D%0A%0D%0A%20%20%3Cxsl%3Amode%20on-no-match%3D%22shallow-copy%22%2F%3E%0D%0A%0D%0A%20%20%3Cxsl%3Atemplate%20match%3D%22%2F%22%20name%3D%22xsl%3Ainitial-template%22%3E%0D%0A%20%20%20%20%3Cxsl%3Anext-match%2F%3E%0D%0A%20%20%20%20%3Cxsl%3Acomment%3ERun%20with%20%7Bsystem-property%28%27xsl%3Aproduct-name%27%29%7D%20%7Bsystem-property%28%27xsl%3Aproduct-version%27%29%7D%20%7Bsystem-property%28%27Q%7Bhttp%3A%2F%2Fsaxon.sf.net%2F%7Dplatform%27%29%7D%3C%2Fxsl%3Acomment%3E%0D%0A%20%20%3C%2Fxsl%3Atemplate%3E%0D%0A%20%20%0D%0A%3C%2Fxsl%3Astylesheet%3E&input=%3Croot%3E%0D%0A%20%20%20%20%3Cfoo%2F%3E%0D%0A%20%20%20%20%3Cfoo%2F%3E%0D%0A%20%20%20%20%3Cfoo%2F%3E%0D%0A%20%20%20%20%3Cfoo%20bar%3D%221%22%2F%3E%0D%0A%20%20%20%20%3Cfoo%20bar%3D%221%22%2F%3E%0D%0A%20%20%20%20%3Cfoo%2F%3E%0D%0A%20%20%20%20%3Cfoo%2F%3E%0D%0A%20%20%20%20%3Cfoo%2F%3E%0D%0A%20%20%20%20%3Cfoo%2F%3E%0D%0A%20%20%20%20%3Cfoo%2F%3E%0D%0A%3C%2Froot%3E&input-type=XML
[2]: https://martin-honnen.github.io/xslt3fiddle/?xslt=%3Cxsl%3Astylesheet%20xmlns%3Axsl%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2FXSL%2FTransform%22%0D%0A%20%20version%3D%223.0%22%0D%0A%20%20xmlns%3Axs%3D%22http%3A%2F%2Fwww.w3.org%2F2001%2FXMLSchema%22%0D%0A%20%20exclude-result-prefixes%3D%22%23all%22%0D%0A%20%20xmlns%3Amf%3D%22http%3A%2F%2Fexample.com%2Fmf%22%0D%0A%20%20expand-text%3D%22yes%22%3E%0D%0A%20%20%0D%0A%20%20%3Cxsl%3Afunction%20name%3D%22mf%3Aadd-attribute%22%20as%3D%22element%28%29%22%3E%0D%0A%20%20%20%20%3Cxsl%3Aparam%20name%3D%22element%22%20as%3D%22element%28%29%22%2F%3E%0D%0A%20%20%20%20%3Cxsl%3Acopy%20select%3D%22%24element%22%3E%0D%0A%20%20%20%20%20%20%3Cxsl%3Aattribute%20name%3D%22wibble%22%20select%3D%222%22%2F%3E%0D%0A%20%20%20%20%20%20%3Cxsl%3Aapply-templates%20select%3D%22%40*%22%2F%3E%0D%0A%20%20%20%20%20%20%3Cxsl%3Aapply-templates%2F%3E%0D%0A%20%20%20%20%3C%2Fxsl%3Acopy%3E%0D%0A%20%20%3C%2Fxsl%3Afunction%3E%0D%0A%20%20%0D%0A%20%20%3Cxsl%3Atemplate%20match%3D%22%2F*%22%3E%0D%0A%20%20%20%20%3Cxsl%3Acopy%3E%0D%0A%20%20%20%20%20%20%3Cxsl%3Aapply-templates%20select%3D%22%40*%22%2F%3E%0D%0A%20%20%20%20%20%20%3Cxsl%3Asequence%20%0D%0A%20%20%20%20%20%20%20%20select%3D%22fold-left%28%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20node%28%29%2C%20%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20map%20%7B%20%27found-foos%27%20%3A%200%2C%20%27nodes%27%20%3A%20%28%29%20%7D%2C%20%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20function%28%24a%2C%20%24n%29%20%7B%20%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20let%20%24is-foo%20%3A%3D%20%24n%20instance%20of%20element%28foo%29%20and%20boolean%28%24n%2Fself%3A%3Afoo%5B%40bar%20%3D%201%5D%29%2C%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24is-first-foo%20%3A%3D%20%24a%3Ffound-foos%20%3D%200%20and%20%24is-foo%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20map%20%7B%20%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%27found-foos%27%20%3A%20if%20%28%24is-foo%29%20then%20%24a%3Ffound-foos%20%2B%201%20else%20%24a%3Ffound-foos%2C%20%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%27nodes%27%3A%20%28%24a%3Fnodes%2C%20if%20%28%24is-first-foo%29%20then%20mf%3Aadd-attribute%28%24n%29%20else%20%24n%29%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%29%3Fnodes%22%2F%3E%0D%0A%20%20%20%20%3C%2Fxsl%3Acopy%3E%0D%0A%20%20%3C%2Fxsl%3Atemplate%3E%0D%0A%0D%0A%20%20%3Cxsl%3Amode%20on-no-match%3D%22shallow-copy%22%2F%3E%0D%0A%0D%0A%20%20%3Cxsl%3Atemplate%20match%3D%22%2F%22%20name%3D%22xsl%3Ainitial-template%22%3E%0D%0A%20%20%20%20%3Cxsl%3Anext-match%2F%3E%0D%0A%20%20%20%20%3Cxsl%3Acomment%3ERun%20with%20%7Bsystem-property%28%27xsl%3Aproduct-name%27%29%7D%20%7Bsystem-property%28%27xsl%3Aproduct-version%27%29%7D%20%7Bsystem-property%28%27Q%7Bhttp%3A%2F%2Fsaxon.sf.net%2F%7Dplatform%27%29%7D%3C%2Fxsl%3Acomment%3E%0D%0A%20%20%3C%2Fxsl%3Atemplate%3E%0D%0A%20%20%0D%0A%3C%2Fxsl%3Astylesheet%3E&input=%3Croot%3E%0D%0A%20%20%20%20%3Cfoo%2F%3E%0D%0A%20%20%20%20%3Cfoo%2F%3E%0D%0A%20%20%20%20%3Cfoo%2F%3E%0D%0A%20%20%20%20%3Cfoo%20bar%3D%221%22%2F%3E%0D%0A%20%20%20%20%3Cfoo%20bar%3D%221%22%2F%3E%0D%0A%20%20%20%20%3Cfoo%2F%3E%0D%0A%20%20%20%20%3Cfoo%2F%3E%0D%0A%20%20%20%20%3Cfoo%2F%3E%0D%0A%20%20%20%20%3Cfoo%2F%3E%0D%0A%20%20%20%20%3Cfoo%2F%3E%0D%0A%3C%2Froot%3E&input-type=XML
[3]: https://martin-honnen.github.io/xslt3fiddle/?xslt=%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22utf-8%22%3F%3E%0D%0A%3Cxsl%3Astylesheet%20xmlns%3Axsl%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2FXSL%2FTransform%22%0D%0A%20%20version%3D%223.0%22%0D%0A%20%20xmlns%3Axs%3D%22http%3A%2F%2Fwww.w3.org%2F2001%2FXMLSchema%22%0D%0A%20%20exclude-result-prefixes%3D%22%23all%22%0D%0A%20%20expand-text%3D%22yes%22%3E%0D%0A%20%20%0D%0A%20%20%3Cxsl%3Aparam%20name%3D%22pattern%22%20static%3D%22yes%22%20as%3D%22xs%3Astring%22%20select%3D%22%27foo%5B%40bar%20%3D%201%5D%5B1%5D%27%22%2F%3E%0D%0A%20%20%0D%0A%20%20%3Cxsl%3Aaccumulator%20name%3D%22have-first-foo-bar%22%20as%3D%22xs%3Aboolean%22%20initial-value%3D%22false%28%29%22%3E%0D%0A%20%20%20%20%3Cxsl%3Aaccumulator-rule%20_match%3D%22%7B%24pattern%7D%22%20select%3D%22true%28%29%22%2F%3E%0D%0A%20%20%20%20%3Cxsl%3Aaccumulator-rule%20phase%3D%22end%22%20_match%3D%22%7B%24pattern%7D%22%20select%3D%22false%28%29%22%2F%3E%0D%0A%20%20%3C%2Fxsl%3Aaccumulator%3E%0D%0A%20%20%0D%0A%20%20%3Cxsl%3Atemplate%20match%3D%22foo%5Baccumulator-before%28%27have-first-foo-bar%27%29%5D%22%3E%0D%0A%20%20%20%20%3Cxsl%3Acopy%3E%0D%0A%20%20%20%20%20%20%3Cxsl%3Aattribute%20name%3D%22wibble%22%20select%3D%222%22%2F%3E%0D%0A%20%20%20%20%20%20%3Cxsl%3Aapply-templates%20select%3D%22%40*%22%2F%3E%0D%0A%20%20%20%20%20%20%3Cxsl%3Aapply-templates%2F%3E%0D%0A%20%20%20%20%3C%2Fxsl%3Acopy%3E%0D%0A%20%20%3C%2Fxsl%3Atemplate%3E%0D%0A%0D%0A%20%20%3Cxsl%3Amode%20on-no-match%3D%22shallow-copy%22%20use-accumulators%3D%22%23all%22%2F%3E%0D%0A%0D%0A%20%20%3Cxsl%3Atemplate%20match%3D%22%2F%22%20name%3D%22xsl%3Ainitial-template%22%3E%0D%0A%20%20%20%20%3Cxsl%3Anext-match%2F%3E%0D%0A%20%20%20%20%3Cxsl%3Acomment%3ERun%20with%20%7Bsystem-property%28%27xsl%3Aproduct-name%27%29%7D%20%7Bsystem-property%28%27xsl%3Aproduct-version%27%29%7D%20%7Bsystem-property%28%27Q%7Bhttp%3A%2F%2Fsaxon.sf.net%2F%7Dplatform%27%29%7D%3C%2Fxsl%3Acomment%3E%0D%0A%20%20%3C%2Fxsl%3Atemplate%3E%0D%0A%20%20%0D%0A%3C%2Fxsl%3Astylesheet%3E%0D%0A&input=%3Croot%3E%0D%0A%20%20%20%20%3Cfoo%2F%3E%0D%0A%20%20%20%20%3Cfoo%2F%3E%0D%0A%20%20%20%20%3Cfoo%2F%3E%0D%0A%20%20%20%20%3Cfoo%20bar%3D%221%22%2F%3E%0D%0A%20%20%20%20%3Cfoo%20bar%3D%221%22%2F%3E%0D%0A%20%20%20%20%3Cfoo%2F%3E%0D%0A%20%20%20%20%3Cfoo%2F%3E%0D%0A%20%20%20%20%3Cfoo%2F%3E%0D%0A%20%20%20%20%3Cfoo%2F%3E%0D%0A%20%20%20%20%3Cfoo%2F%3E%0D%0A%3C%2Froot%3E&input-type=XML
</details>
# 答案2
**得分**: 0
以下是翻译好的部分:
A pattern I sometimes use for this is a global variable combined with a template rule:
```xml
<xsl:variable name="special-nodes" select="//foo[@bar='1'][1]"/>
<xsl:template match="$special-nodes">...</xsl:template>
It only works, of course, in a "single document" scenario where the global variable applies to the same document that you're processing with the template rule.
英文:
A pattern I sometimes use for this is a global variable combined with a template rule:
<xsl:variable name="special-nodes" select="//foo[@bar='1'][1]"/>
<xsl:template match="$special-nodes">...</xsl:template>
It only works, of course, in a "single document" scenario where the global variable applies to the same document that you're processing with the template rule.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论