我正在尝试匹配一组特定的元素,但只是那些是另一个元素结构的子元素(让我们说它的输入或仅在div
s内的某个地方选择元素“class-sauce”在他们)。通常情况下,就XPATH而言,这很容易:我们可以通过括号方式联合目标儿童,如下所示:
div[contains(@class, 'special-sauce')//(input | select)
但是当我们尝试将此作为模板匹配时(至少在Saxon中),这是XSLT抛出曲线球的地方:
<xsl:template match="div[contains(@class, 'special-sauce')//(input | select)">
{“error”:“无法解析xsl文件(/section-settings.xsl)。 无法编译样式表。 1错误 检测 “” 代码 “:” TRANSFORM_ERROR “ ”位置“:空 ”使“:[” 致命 错误:令牌\“(\”这里不允许使用XSLT模式“]}
基本上,括号不允许作为主路径级别的模板匹配的一部分(显然它们在条件/等内部仍然可以正常工作)。
那该怎么办?
嗯,从技术上讲,使用联合仍然可以工作,但我们每次都必须重复祖先XPATH,因为我们不能用括号括起孩子:
<xsl:template match="div[contains(@class, 'special-sauce')//input
| div[contains(@class, 'special-sauce')//select">
这是可行的(不是很漂亮,但确定,我们可以处理这些!换行可以在这里帮助我们的理智yay)在我们这里的简单示例中,但是对于更复杂的XPATH会有问题,特别是如果括号结合可能是在较长的xpath中间,或者是在很多元素中。
e.g。
div[contains(@class, 'major-mess')]/div[contains(@class, 'special-sauce')]//(dataset | optgroup | fieldset)//(button | option | label)
变为
a crazy mess.
好吧,在更复杂的例子中,这很快就会成为一种选择。虽然以不同方式构造我们的XSLT可能有所帮助(中间匹配,使用模态等),但问题仍然存在:
第一个示例的示例表:
<div class="special-sauce">
<input class="form-control" type="text" value="" placeholder="INHERITED:" />
<select class="form-control">
<option value="INHERITED: ">INHERIT: </option>
<option value=""></option>
</select>
<div class="radio">
<label>
<input type="radio" name="param3vals" value="INHERITED: " />
INHERIT:
</label>
</div>
</div>
<div class="not-special"><input type="text" id="contact-info-include-path" size="90">
<label>contact</label>
</input></div>
<div class="sad-panda"><input type="text" id="sidenav-include-path" size="90">
<label>sidenav</label>
</input></div>
注意:这确实假设身份转换正在作为处理输入文档的主要方法运行。
虽然还有其他问题可以有效地得到类似的答案,例如我在下面给出的答案,但我觉得这些问题的背景通常更为笼统(这样一个顶级联盟就可以作为他们的回答并发症),更具体的方式不匹配,或根本不同。因此Q&amp; A格式。
答案 0 :(得分:4)
我看了一遍,大多数类似问题的答案都涉及将XPATH的重复部分复制到每个元素并将它们组合在一起。但有更好的方法!很容易忘记匹配特定元素相对于在XPATH中匹配该元素的名称。
name()
或local-name()
而不是直接在模板匹配模式*中匹配元素。在选择要使用的内容时,请注意您的命名空间问题/需求。这仍然允许对这些元素的属性/等进行高级条件化。
例如,第一场比赛变为:
<xsl:template match="div[contains(@class, 'special-sauce')//
element()[local-name() = ('input', 'select')]">
在空间或时间方面没有大幅增加来解决这个问题,但是我们做减少了冗余以及可能导致的相关数据一致性错误(经常发生,特别是如果以后制作的话)变化)。
真正闪耀的是问题中的最后一个例子(混乱):
<xsl:template match="div[contains(@class, 'major-mess')]/
div[contains(@class, 'special-sauce')]//
element()[local-name() = ('dataset', 'optgroup', 'fieldset')]//
element()[local-name() = ('button', 'option', 'label')]">
由于我不记得是否完全符合XSLT / XPATH 1.0,通过括号创建元素树片段进行比较,如果你确实需要向后兼容“contains()
与包围分隔符”(减少机会)来自另一个元素的误报是目标全名的子字符串模式总是有效:
<xsl:template match="div[contains(@class, 'major-mess')]/
div[contains(@class, 'special-sauce')]//
element()[contains('|dataset|optgroup|fieldset|'), concat('|', local-name(), '|'))]//
element()[contains('|button|option|label|', concat('|', local-name(), '|'))]">
如果您正在努力理解为什么天真的方法(我在问题中尝试的第一件事)在XSLT中失败,那么有助于理解像“匹配”这样的模板规则必须遵循XSLT patterns,这只是expression tokens本质上是一组有效的XPath表达式(这很容易使得区分和记忆更加困惑,特别是当很多来源只是假装它完全是XPath时)。请注意,括号仅显示为用作predicates的有效选项,仅在{{3}}内的表达式中找到,而不是位置路径或位置步骤的任何其他部分。
最终注意事项
性能:我不知道这种方法是否存在显着的性能差异,而不是将每个单独的元素组合为每个元素的完整路径,或者在本地寻址元素与作为谓词之间是否存在真正的性能差异匿名element()
选择器。我怀疑,虽然大多数XSLT处理器在使用本机路径结构编写单个匹配时可能实现更快的DOM树搜索,而在匿名选择器上使用name()函数编写谓词,但联合情况可能会执行得更快,具体取决于如何处理器尝试预编译和优化逻辑模式。我将把这项任务留给别人去尝试基准测试,因为最终真正的障碍就是开发人员的理智和维护问题(可能会引发人为错误)。在复杂的匹配中,我觉得这种方法的简单易读性和减少/消除的数据冗余可以轻松地满足任何小的性能损失。