我发现Schematron忽略了任何具有context
属性的规则,该属性包含对属性的引用。在下面的代码中,我有@att
,但任何包含属性的复杂xpath都会遇到问题。 (例如bar/@att
也会被忽略。)
我使用的是Schematron分发版here。
我有test.sch
:
<?xml version="1.0" encoding="utf-8"?>
<schema
xmlns="http://purl.oclc.org/dsdl/schematron"
queryBinding="xslt2"
schemaVersion="ISO19757-3">
<title>Test schema.</title>
<pattern>
<rule context="@att">
<assert test="false()">@att cannot appear anywhere.</assert>
</rule>
<rule context="foo">
<assert test="false()">foo cannot appear anywhere.</assert>
</rule>
</pattern>
</schema>
此test.xml
文件:
<foo att="something"/>
为了可复制性,这个Makefile
:
SAXON:=saxon
SCHEMATRON_TO_XSL:=/home/ldd/src/schematron/xslt/iso_svrl_for_xslt2.xsl
.PHONY: all
all: test.xsl test.xml
$(SAXON) -xsl:$< -s:$(word 2,$^)
test.xsl: test.sch
$(SAXON) -s:$< -o:$@ -xsl:$(SCHEMATRON_TO_XSL) allow-foreign=true generate-fired-rule=false
.PHONY: clean
clean:
rm test.xsl
如果我发出make clean; make
,我会得到一个失败的断言:
<svrl:failed-assert test="false()" location="/foo[1]">
<svrl:text>foo cannot appear anywhere.</svrl:text>
</svrl:failed-assert>
Schematron忽略了<rule context="@att">
中的测试,该测试也应该失败。为什么忽略该规则?
请注意,如果我将test.sch
更改为使用queryBinding="xslt1"
并编辑Makefile
以更改SCHEMATRON_TO_XSL
以使用XSLT 1的schematron转换,那么我确实会遇到两个失败我期待的。所以只有在使用XSLT 2时才会出现问题。
答案 0 :(得分:3)
这是Schematron中的一个错误,似乎至少自2010年以来一直存在于Schematron的代码中,当然也就是自2011年以来。(自从AFAIK发布以来,很难说Schematron没有公共版本控制存储库看看这个答案的结尾,知道为什么我说自2011年以来当然存在。)
如果您检查从Schematron生成的test.xsl
文件,您会看到xsl:apply-templates
这样的元素:
<xsl:apply-templates select="*|comment()|processing-instruction()" mode="M1"/>
请注意属性节点不属于select
。
运行saxon
转换test.sch
文件时执行的代码导入名为iso_schematron_skeleton_for_saxon.xsl
的架构。如果您检查该文件,您会发现XSL中出现的xsl:apply-template
元素是使用以下代码生成的:
<axsl:apply-templates select="{$context-xpath}" mode="M{count(preceding-sibling::*)}"/>
(axsl:
不是拼写错误。它是输出节点使用的名称空间前缀。)
在同一个文件中向后看,你会发现:
<xsl:variable name="context-xpath">
<xsl:if test="$attributes='true' and parent::node() ">@*|</xsl:if>
<xsl:choose>
<xsl:when test="$only-child-elements='true'">*</xsl:when>
<xsl:when test="$visit-text='true'">node()</xsl:when>
<xsl:otherwise>*|comment()|processing-instruction()</xsl:otherwise>
</xsl:choose>
</xsl:variable>
和
<xsl:param name="attributes">
<xsl:choose>
<xsl:when test="//iso:rule[contains(@context,'@') or contains(@context,'attribute')]">true</xsl:when>
<xsl:otherwise>false</xsl:otherwise>
</xsl:choose>
</xsl:param>
问题在于context-xpath
的初始化方式。它是一个顶级变量,初始化一次,只有一次。在初始化时,上下文节点是文档的节点,因此parent::node()
始终为false,因此@*|
永远不会包含context-xpath
。
根据我的经验attributes
已正确初始化。所以没有必要摆弄它。
请注意,当您运行XSLT 1处理器的Schematron样式表时,会使用不同的代码库,但没有此错误。 (相当于iso_schematron_skeleton_for_saxon.xsl
的XSLT 1是iso_schematron_skeleton_for_xslt1.xsl
)。
可以重写有问题的规则,使它们没有引用属性的context
。我不能说我是这个解决方案的粉丝,因为它使规则复杂化并可能产生严重的负面性能影响。并且可能存在重写规则的情况。
iso_schematron_skeleton_for_saxon.xsl
我在编辑文件时不再遇到问题,因此以这种方式初始化context-path
:
<xsl:variable name="context-xpath">
<xsl:if test="$attributes='true'">@*|</xsl:if>
<xsl:choose>
<xsl:when test="$only-child-elements='true'">*</xsl:when>
<xsl:when test="$visit-text='true'">node()</xsl:when>
<xsl:otherwise>*|comment()|processing-instruction()</xsl:otherwise>
</xsl:choose>
</xsl:variable>
一般来说,我不喜欢对第三方代码进行临时编辑,但我没有看到更好的解决方案。
在完成上述调查后,我很清楚哪些关键字可以帮助我找到有关该主题的任何问题报告,因此我开始搜索context-xpath
和类似的关键字。我发现Ken Holman在2011年12月遇到了同样的问题,this email证明了这一点。他得出的结论与我的相似。他还建议强制attributes
参数为真。根据我的经验,没有必要。他提到的案例是Schematron代码在多个文件中分割的情况,但Schematron的readme.txt
表示处理的第一步应该是运行iso_dsdl_include.xsl
以将模式的多个部分组合到一个文件中
AFAIK Ken Holman的电子邮件从未收到回复,问题从未在任何“官方”消息来源中修复。