避免 XSLT 中的循环键定义
Avoiding circular key definitions in XSLT
我正在编写一些代码,将有效的 XML 实例转换为 DITA 代码块中的代码表示。
我的输入中有一些包装元素,允许我定义一些对输出的强调。这对于 node() 项目来说很容易,因为包装器直接包装了要强调的代码。但是,对于属性等,我需要在强调元素上指定一些 @select。
以下是我如何尝试执行此操作的简短代码摘录(对于属性;我已经删除了其他类型内容的类似模板):
<xsl:key name="emph" match="eg:emph[@select]">
<xsl:variable name="selected">
<xsl:evaluate xpath="@select" context-item="."/>
</xsl:variable>
<xsl:for-each select="$selected">
<xsl:sequence select="generate-id()"/>
</xsl:for-each>
</xsl:key>
<xsl:template match="@*[key('emph', generate-id(.))]" mode="eg">
<xsl:variable name="style" select="if (not(key('emph', generate-id(.))/@style)) then 'italic' else key('emph', generate-id())/@style"/>
<xsl:text> </xsl:text>
<ph outputclass="{$style}">
<xsl:next-match>
<xsl:with-param name="includeSpace" select="false()"/>
</xsl:next-match>
</ph>
</xsl:template>
<xsl:template match="@*" mode="eg">
<xsl:param name="includeSpace" as="xs:boolean" select="true()"/>
<xsl:if test="$includeSpace">
<xsl:text> </xsl:text>
</xsl:if>
<ph outputclass="AttributeName">
<xsl:value-of select="name()"/>
</ph>
<ph outputclass="equals">=</ph>
<ph outputclass="attributeQuotes">"</ph>
<ph outputclass="AttributeValue">
<xsl:value-of select="."/>
</ph>
<ph outputclass="attributeQuotes">"</ph>
</xsl:template>
输入如下:
<eg:emph select="abbrev-journal-title/@abbrev-type">
<abbrev-journal-title abbrev-type="custom">JPhysA</abbrev-journal-title>
</eg:emph>
我想生成如下内容:
<ph outputclass="XmlFurniture"><</ph><ph outputclass="ElementName">abbrev-journal-title</ph> <ph outputclass="italic"><ph outputclass="AttributeName">abbrev-type</ph><ph outputclass="equals">=</ph><ph outputclass="attributeQuotes">"</ph><ph outputclass="AttributeValue">custom</ph><ph outputclass="attributeQuotes">"</ph></ph><ph outputclass="XmlFurniture">></ph>JPhysA<ph outputclass="XmlFurniture"></</ph><ph outputclass="ElementName">abbrev-journal-title</ph><ph outputclass="XmlFurniture">></ph>
Transforming with Saxon (PE 9.8.0.12) returns 'key definition is circular' 错误 - 但据我所知,事实并非如此。
任何人都可以提出解决方法,或者至少解释为什么这种方法不起作用?
嗯,关于 xsl:evaluate
的使用,请参阅 https://www.w3.org/TR/xslt-30/#evaluate-dynamic-context 解释
The context item, position, and size depend on the result of
evaluating the expression in the context-item
attribute. If this
attribute is absent, or if the result is an empty sequence, then the
context item, position, and size for evaluation of the target
expression are all absent.
由于您根本没有在 xsl:evaluate
上设置 context-item
属性,因此您对 xpath="@select"
的尝试没有任何意义,我猜您想使用 context-item="."
到 select 匹配的元素。
至于select将属性节点存储在变量中,我想你需要使用
<xsl:variable name="selected" as="attribute()*">
<xsl:evaluate xpath="@select" context-item="."/>
</xsl:variable>
而不是
<xsl:variable name="selected">
<xsl:evaluate xpath="@select" context-item="."/>
</xsl:variable>
那我觉得
<xsl:for-each select="$selected">
<xsl:sequence select="generate-id()"/>
</xsl:for-each>
可以是shortened/simplified到
<xsl:sequence select="$selected!generate-id()"/>
在 XSLT 3 的上下文中。
我现在尝试构建一个最小但完整的示例,并使用 Saxon 进行测试,使用 Saxon 9.8.0.12 EE 和 Saxon 9.9.0.1 EE,我没有收到任何错误,而且该方法似乎有效使用您创建的模板。
测试 XSLT 是:
<?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:eg="http://example.com/eg"
exclude-result-prefixes="#all"
default-mode="eg"
version="3.0">
<xsl:mode name="eg" on-no-match="shallow-copy"/>
<xsl:output indent="yes"/>
<xsl:key name="emph" match="eg:emph[@select]">
<xsl:variable name="selected" as="attribute()*">
<xsl:evaluate xpath="@select" context-item="."/>
</xsl:variable>
<xsl:sequence select="$selected ! generate-id()"/>
</xsl:key>
<xsl:template match="@*[key('emph', generate-id(.))]" mode="eg">
<xsl:variable name="style" select="if (not(key('emph', generate-id(.))/@style)) then 'italic' else key('emph', generate-id())/@style"/>
<xsl:text> </xsl:text>
<ph outputclass="{$style}">
<xsl:next-match>
<xsl:with-param name="includeSpace" select="false()"/>
</xsl:next-match>
</ph>
</xsl:template>
<xsl:template match="@*" mode="eg">
<xsl:param name="includeSpace" as="xs:boolean" select="true()"/>
<xsl:if test="$includeSpace">
<xsl:text> </xsl:text>
</xsl:if>
<ph outputclass="AttributeName">
<xsl:value-of select="name()"/>
</ph>
<ph outputclass="equals">=</ph>
<ph outputclass="attributeQuotes">"</ph>
<ph outputclass="AttributeValue">
<xsl:value-of select="."/>
</ph>
<ph outputclass="attributeQuotes">"</ph>
</xsl:template>
</xsl:stylesheet>
样本输入是
<root>
<eg:emph select="abbrev-journal-title/@abbrev-type" xmlns:eg="http://example.com/eg">
<abbrev-journal-title abbrev-type="custom">JPhysA</abbrev-journal-title>
</eg:emph>
<eg:emph select="abbrev-journal-title/@abbrev-type" xmlns:eg="http://example.com/eg" style="bold">
<abbrev-journal-title abbrev-type="custom">JPhysA</abbrev-journal-title>
</eg:emph>
</root>
结果显示属性已匹配和转换:
<root>
<eg:emph xmlns:eg="http://example.com/eg">
<ph outputclass="AttributeName">select</ph>
<ph outputclass="equals">=</ph>
<ph outputclass="attributeQuotes">"</ph>
<ph outputclass="AttributeValue">abbrev-journal-title/@abbrev-type</ph>
<ph outputclass="attributeQuotes">"</ph>
<abbrev-journal-title>
<ph outputclass="italic">
<ph outputclass="AttributeName">abbrev-type</ph>
<ph outputclass="equals">=</ph>
<ph outputclass="attributeQuotes">"</ph>
<ph outputclass="AttributeValue">custom</ph>
<ph outputclass="attributeQuotes">"</ph>
</ph>JPhysA</abbrev-journal-title>
</eg:emph>
<eg:emph xmlns:eg="http://example.com/eg">
<ph outputclass="AttributeName">select</ph>
<ph outputclass="equals">=</ph>
<ph outputclass="attributeQuotes">"</ph>
<ph outputclass="AttributeValue">abbrev-journal-title/@abbrev-type</ph>
<ph outputclass="attributeQuotes">"</ph>
<ph outputclass="AttributeName">style</ph>
<ph outputclass="equals">=</ph>
<ph outputclass="attributeQuotes">"</ph>
<ph outputclass="AttributeValue">bold</ph>
<ph outputclass="attributeQuotes">"</ph>
<abbrev-journal-title>
<ph outputclass="bold">
<ph outputclass="AttributeName">abbrev-type</ph>
<ph outputclass="equals">=</ph>
<ph outputclass="attributeQuotes">"</ph>
<ph outputclass="AttributeValue">custom</ph>
<ph outputclass="attributeQuotes">"</ph>
</ph>JPhysA</abbrev-journal-title>
</eg:emph>
</root>
我正在编写一些代码,将有效的 XML 实例转换为 DITA 代码块中的代码表示。
我的输入中有一些包装元素,允许我定义一些对输出的强调。这对于 node() 项目来说很容易,因为包装器直接包装了要强调的代码。但是,对于属性等,我需要在强调元素上指定一些 @select。
以下是我如何尝试执行此操作的简短代码摘录(对于属性;我已经删除了其他类型内容的类似模板):
<xsl:key name="emph" match="eg:emph[@select]">
<xsl:variable name="selected">
<xsl:evaluate xpath="@select" context-item="."/>
</xsl:variable>
<xsl:for-each select="$selected">
<xsl:sequence select="generate-id()"/>
</xsl:for-each>
</xsl:key>
<xsl:template match="@*[key('emph', generate-id(.))]" mode="eg">
<xsl:variable name="style" select="if (not(key('emph', generate-id(.))/@style)) then 'italic' else key('emph', generate-id())/@style"/>
<xsl:text> </xsl:text>
<ph outputclass="{$style}">
<xsl:next-match>
<xsl:with-param name="includeSpace" select="false()"/>
</xsl:next-match>
</ph>
</xsl:template>
<xsl:template match="@*" mode="eg">
<xsl:param name="includeSpace" as="xs:boolean" select="true()"/>
<xsl:if test="$includeSpace">
<xsl:text> </xsl:text>
</xsl:if>
<ph outputclass="AttributeName">
<xsl:value-of select="name()"/>
</ph>
<ph outputclass="equals">=</ph>
<ph outputclass="attributeQuotes">"</ph>
<ph outputclass="AttributeValue">
<xsl:value-of select="."/>
</ph>
<ph outputclass="attributeQuotes">"</ph>
</xsl:template>
输入如下:
<eg:emph select="abbrev-journal-title/@abbrev-type">
<abbrev-journal-title abbrev-type="custom">JPhysA</abbrev-journal-title>
</eg:emph>
我想生成如下内容:
<ph outputclass="XmlFurniture"><</ph><ph outputclass="ElementName">abbrev-journal-title</ph> <ph outputclass="italic"><ph outputclass="AttributeName">abbrev-type</ph><ph outputclass="equals">=</ph><ph outputclass="attributeQuotes">"</ph><ph outputclass="AttributeValue">custom</ph><ph outputclass="attributeQuotes">"</ph></ph><ph outputclass="XmlFurniture">></ph>JPhysA<ph outputclass="XmlFurniture"></</ph><ph outputclass="ElementName">abbrev-journal-title</ph><ph outputclass="XmlFurniture">></ph>
Transforming with Saxon (PE 9.8.0.12) returns 'key definition is circular' 错误 - 但据我所知,事实并非如此。
任何人都可以提出解决方法,或者至少解释为什么这种方法不起作用?
嗯,关于 xsl:evaluate
的使用,请参阅 https://www.w3.org/TR/xslt-30/#evaluate-dynamic-context 解释
The context item, position, and size depend on the result of evaluating the expression in the
context-item
attribute. If this attribute is absent, or if the result is an empty sequence, then the context item, position, and size for evaluation of the target expression are all absent.
由于您根本没有在 xsl:evaluate
上设置 context-item
属性,因此您对 xpath="@select"
的尝试没有任何意义,我猜您想使用 context-item="."
到 select 匹配的元素。
至于select将属性节点存储在变量中,我想你需要使用
<xsl:variable name="selected" as="attribute()*">
<xsl:evaluate xpath="@select" context-item="."/>
</xsl:variable>
而不是
<xsl:variable name="selected">
<xsl:evaluate xpath="@select" context-item="."/>
</xsl:variable>
那我觉得
<xsl:for-each select="$selected">
<xsl:sequence select="generate-id()"/>
</xsl:for-each>
可以是shortened/simplified到
<xsl:sequence select="$selected!generate-id()"/>
在 XSLT 3 的上下文中。
我现在尝试构建一个最小但完整的示例,并使用 Saxon 进行测试,使用 Saxon 9.8.0.12 EE 和 Saxon 9.9.0.1 EE,我没有收到任何错误,而且该方法似乎有效使用您创建的模板。
测试 XSLT 是:
<?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:eg="http://example.com/eg"
exclude-result-prefixes="#all"
default-mode="eg"
version="3.0">
<xsl:mode name="eg" on-no-match="shallow-copy"/>
<xsl:output indent="yes"/>
<xsl:key name="emph" match="eg:emph[@select]">
<xsl:variable name="selected" as="attribute()*">
<xsl:evaluate xpath="@select" context-item="."/>
</xsl:variable>
<xsl:sequence select="$selected ! generate-id()"/>
</xsl:key>
<xsl:template match="@*[key('emph', generate-id(.))]" mode="eg">
<xsl:variable name="style" select="if (not(key('emph', generate-id(.))/@style)) then 'italic' else key('emph', generate-id())/@style"/>
<xsl:text> </xsl:text>
<ph outputclass="{$style}">
<xsl:next-match>
<xsl:with-param name="includeSpace" select="false()"/>
</xsl:next-match>
</ph>
</xsl:template>
<xsl:template match="@*" mode="eg">
<xsl:param name="includeSpace" as="xs:boolean" select="true()"/>
<xsl:if test="$includeSpace">
<xsl:text> </xsl:text>
</xsl:if>
<ph outputclass="AttributeName">
<xsl:value-of select="name()"/>
</ph>
<ph outputclass="equals">=</ph>
<ph outputclass="attributeQuotes">"</ph>
<ph outputclass="AttributeValue">
<xsl:value-of select="."/>
</ph>
<ph outputclass="attributeQuotes">"</ph>
</xsl:template>
</xsl:stylesheet>
样本输入是
<root>
<eg:emph select="abbrev-journal-title/@abbrev-type" xmlns:eg="http://example.com/eg">
<abbrev-journal-title abbrev-type="custom">JPhysA</abbrev-journal-title>
</eg:emph>
<eg:emph select="abbrev-journal-title/@abbrev-type" xmlns:eg="http://example.com/eg" style="bold">
<abbrev-journal-title abbrev-type="custom">JPhysA</abbrev-journal-title>
</eg:emph>
</root>
结果显示属性已匹配和转换:
<root>
<eg:emph xmlns:eg="http://example.com/eg">
<ph outputclass="AttributeName">select</ph>
<ph outputclass="equals">=</ph>
<ph outputclass="attributeQuotes">"</ph>
<ph outputclass="AttributeValue">abbrev-journal-title/@abbrev-type</ph>
<ph outputclass="attributeQuotes">"</ph>
<abbrev-journal-title>
<ph outputclass="italic">
<ph outputclass="AttributeName">abbrev-type</ph>
<ph outputclass="equals">=</ph>
<ph outputclass="attributeQuotes">"</ph>
<ph outputclass="AttributeValue">custom</ph>
<ph outputclass="attributeQuotes">"</ph>
</ph>JPhysA</abbrev-journal-title>
</eg:emph>
<eg:emph xmlns:eg="http://example.com/eg">
<ph outputclass="AttributeName">select</ph>
<ph outputclass="equals">=</ph>
<ph outputclass="attributeQuotes">"</ph>
<ph outputclass="AttributeValue">abbrev-journal-title/@abbrev-type</ph>
<ph outputclass="attributeQuotes">"</ph>
<ph outputclass="AttributeName">style</ph>
<ph outputclass="equals">=</ph>
<ph outputclass="attributeQuotes">"</ph>
<ph outputclass="AttributeValue">bold</ph>
<ph outputclass="attributeQuotes">"</ph>
<abbrev-journal-title>
<ph outputclass="bold">
<ph outputclass="AttributeName">abbrev-type</ph>
<ph outputclass="equals">=</ph>
<ph outputclass="attributeQuotes">"</ph>
<ph outputclass="AttributeValue">custom</ph>
<ph outputclass="attributeQuotes">"</ph>
</ph>JPhysA</abbrev-journal-title>
</eg:emph>
</root>