XSLT Schema-aware模式下最具体子类型模板匹配问题问询
我的XSD中存在如下类型层级:RetentionContest 继承自 BallotMeasureContest 继承自 Contest。我编写了多个XSLT模板,例如:
<xsl:template name="cdf:BallotMeasureContest" match="element(*, cdf:BallotMeasureContest)">
以及:
<xsl:template name="cdf:RetentionContest" match="element(*, cdf:RetentionContest)">
当输入元素的@xsi:type = "cdf:RetentionContest"(原输入拼写有误,已修正为RetentionContest),且使用Schema-aware处理器时,哪个模板会被调用?如何确保最具体的子类型对应的模板被执行?(备注:我尝试过设置优先级但效果不佳,Altova和Saxon-EE似乎只会调用最后一个适用的模板。)
复现代码
repro_delete.xml
<?xml version="1.0" encoding="UTF-8"?> <n1:ElectionReport xmlns:n1="http://itl.nist.gov/ns/voting/1500-100/v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://itl.nist.gov/ns/voting/1500-100/v2 repro_delete.xsd"> <n1:Election> <n1:Contest xsi:type="n1:RetentionContest" ObjectId="rc-123"> <n1:CandidateId>rc-123</n1:CandidateId> </n1:Contest> </n1:Election> </n1:ElectionReport>
repro_delete.xsl
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns="http://www.w3.org/2005/xpath-functions" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cdf="http://itl.nist.gov/ns/voting/1500-100/v2" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:math="http://www.w3.org/2005/xpath-functions/math" xmlns:array="http://www.w3.org/2005/xpath-functions/array" xmlns:map="http://www.w3.org/2005/xpath-functions/map" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:err="http://www.w3.org/2005/xqt-errors" exclude-result-prefixes="array cdf fn map math xhtml err xs xsi" version="3.0"> <xsl:output method="text" version="1.0" encoding="UTF-8" indent="yes"/> <xsl:import-schema namespace="http://itl.nist.gov/ns/voting/1500-100/v2" schema-location="repro_delete.xsd"/> <xsl:template match="/" name="xsl:initial-template"> <xsl:apply-templates/> </xsl:template> <xsl:template name="cdf:C" match="element(*, cdf:Contest)"> Contest </xsl:template> <xsl:template name="cdf:RC" match="element(*, cdf:RetentionContest)"> Retention </xsl:template> <xsl:template name="cdf:BMC" match="element(*, cdf:BallotMeasureContest)"> Ballot Measure Contest </xsl:template> </xsl:stylesheet>
repro_delete.xsd
<?xml version="1.0" encoding="UTF-8"?> <xsd:schema xmlns="http://itl.nist.gov/ns/voting/1500-100/v2" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://itl.nist.gov/ns/voting/1500-100/v2" elementFormDefault="qualified" version="2.0.3"> <xsd:element name="ElectionReport" type="ElectionReport"/> <xsd:complexType name="BallotMeasureContest"> <xsd:complexContent> <xsd:extension base="Contest"> <xsd:sequence> <xsd:element name="FullText" type="InternationalizedText" minOccurs="0" > </xsd:element> </xsd:sequence> </xsd:extension> </xsd:complexContent> </xsd:complexType> <xsd:complexType name="Contest" abstract="true"> <xsd:sequence> <xsd:element name="SubUnitsReported" type="xsd:integer" minOccurs="0"> </xsd:element> <xsd:element name="TotalSubUnits" type="xsd:integer" minOccurs="0"> </xsd:element> </xsd:sequence> <xsd:attribute name="ObjectId" type="xsd:ID" use="required"/> </xsd:complexType> <xsd:complexType name="Election"> <xsd:sequence> <xsd:element name="Contest" type="Contest" minOccurs="0" maxOccurs="unbounded" > </xsd:element> </xsd:sequence> </xsd:complexType> <xsd:complexType name="ElectionReport"> <xsd:sequence> <xsd:element name="Election" type="Election" minOccurs="0" maxOccurs="unbounded" > </xsd:element> </xsd:sequence> </xsd:complexType> <xsd:complexType name="InternationalizedText"> <xsd:sequence> <xsd:element name="Text" type="xsd:string" maxOccurs="unbounded"> </xsd:element> </xsd:sequence> <xsd:attribute name="Label" type="xsd:string"> </xsd:attribute> </xsd:complexType> <xsd:complexType name="RetentionContest"> <xsd:complexContent> <xsd:extension base="BallotMeasureContest"> <xsd:sequence> <xsd:element name="CandidateId" type="xsd:IDREF"> </xsd:element> </xsd:sequence> </xsd:extension> </xsd:complexContent> </xsd:complexType> </xsd:schema>
问题解答
在Schema-aware的XSLT处理中,当元素通过xsi:type指定为子类型时,匹配父/子类型的模板都会成为候选。但XSLT默认优先级规则不会自动为element(*, type)模式根据类型继承关系赋予更高优先级,这就是你设置优先级无效、处理器选择最后声明模板的原因。
要确保最具体的子类型模板被调用,可采用以下两种可靠方法:
1. 显式设置递增的模板优先级
给匹配子类型的模板设置比父类型更高的priority值,比如:
<xsl:template name="cdf:C" match="element(*, cdf:Contest)" priority="1"/> <xsl:template name="cdf:BMC" match="element(*, cdf:BallotMeasureContest)" priority="2"/> <xsl:template name="cdf:RC" match="element(*, cdf:RetentionContest)" priority="3"/>
优先级数值越大,模板越优先被选择,需保证每个子类型的优先级都高于其父类型。
2. 结合xsl:next-match实现层次调用(可选)
如果需要同时执行子类型和父类型模板的逻辑,可在子类型模板中用xsl:next-match触发父类型模板的处理:
<xsl:template name="cdf:RC" match="element(*, cdf:RetentionContest)" priority="3"> Retention <xsl:next-match/> <!-- 触发BallotMeasureContest模板的执行 --> </xsl:template>
若只需执行最具体的模板,第一种方法更直接高效。
需要注意的是,不同处理器对element(*, type)模式的优先级计算可能存在差异,显式设置优先级是跨处理器兼容的最可靠方案,能确保在Altova和Saxon-EE中都正确生效。
内容的提问来源于stack exchange,提问作者J. Nicholas

