Schematron术语对序列访问异常:推荐术语无法正常获取
解决Schematron中术语对序列无法获取推荐术语的问题
问题根源
你定义的$termPairs是序列的序列(每个术语对是两个字符串组成的子序列),原代码中两次遍历序列的方式存在逻辑冗余,且在推荐术语匹配时可能因序列比较的隐式处理导致匹配失败。另外,原代码中contains(current(), $pair[1])未显式将节点转为字符串,可能引发潜在的类型问题。
解决方案
以下提供三种可靠的实现方式,按推荐优先级排序:
方案1:使用XPath 3.1 Map(推荐,适用于支持XSLT3的处理器)
Map结构能清晰关联禁用术语和推荐术语,代码可读性更强:
<sch:pattern id="termChecker"> <sch:let name="termPairs" value="( map{'forbidden':'Autobahn', 'recommended':'highway'}, map{'forbidden':'client', 'recommended':'customer'}, map{'forbidden':'newbie', 'recommended':'Beginner'} )"/> <sch:rule context="//p"> <!-- 找到第一个匹配的术语Map --> <sch:let name="matchingPair" value="$termPairs[contains(string(.), ?forbidden)][1]"/> <sch:report role="info" test="$matchingPair"> Warning: This text contains the forbidden word “<sch:value-of select='$matchingPair?forbidden'/>”. Alternative: “<sch:value-of select='$matchingPair?recommended'/>”. </sch:report> </sch:rule> </sch:pattern>
方案2:使用平行序列(兼容XPath 2.0+)
通过两个平行序列分别存储禁用术语和推荐术语,利用位置索引关联对应关系:
<sch:pattern id="termChecker"> <sch:let name="forbiddenTerms" value="('Autobahn', 'client', 'newbie')"/> <sch:let name="recommendedTerms" value="('highway', 'customer', 'Beginner')"/> <sch:rule context="//p"> <!-- 获取匹配的术语索引 --> <sch:let name="matchIndex" value="(for $i in 1 to count($forbiddenTerms) return if (contains(string(.), $forbiddenTerms[$i])) then $i else ())[1]"/> <sch:report role="info" test="$matchIndex"> Warning: This text contains the forbidden word “<sch:value-of select='$forbiddenTerms[$matchIndex]'/>”. Alternative: “<sch:value-of select='$recommendedTerms[$matchIndex]'/>”. </sch:report> </sch:rule> </sch:pattern>
方案3:修正原序列的序列访问逻辑
保留原术语对结构,通过直接过滤子序列简化逻辑:
<sch:pattern id="termChecker"> <sch:let name="termPairs" value="( ('Autobahn', 'highway'), ('client', 'customer'), ('newbie', 'Beginner') )"/> <sch:rule context="//p"> <!-- 过滤出匹配的术语对子序列 --> <sch:let name="matchingPair" value="$termPairs[contains(string(.), .[1])][1]"/> <sch:report role="info" test="$matchingPair"> Warning: This text contains the forbidden word “<sch:value-of select='$matchingPair[1]'/>”. Alternative: “<sch:value-of select='$matchingPair[2]'/>”. </sch:report> </sch:rule> </sch:pattern>
额外优化建议
- 大小写不敏感匹配:如果需要忽略大小写检查,将
contains(string(.), $xxx)改为contains(lower-case(string(.)), lower-case($xxx)) - 全文词匹配:如果需要精准匹配整个单词而非子字符串,可使用正则表达式:
matches(string(.), concat('\b', $forbiddenTerm, '\b'))
内容的提问来源于stack exchange,提问作者Stefan Gentz
相关产品推荐
相关产品推荐

