"Неправильное использование" <xsl: key> для проверки работоспособности
Я использую множество ключей, и иногда я помещаю в них проверки работоспособности, например:
<xsl:key name="foo-with-bar" match="foo[contains(., 'bar')]">
<xsl:if test="@baz='xyz'">
<xsl:message terminate="yes">
Can't handle <foo> containing "bar" if @baz="xyz"
</xsl:message>
</xsl:if>
<xsl:value-of select="generate-id()"/>
</xsl:key>
(Это на самом деле довольно простой тест - реальные тесты могут быть довольно сложными.) Что заставило меня задуматься, почему бы не использовать ключи в тех случаях, когда мне фактически не нужен ключ, а только проверка работоспособности? Например:
<xsl:key name="sanity-check" match="foo[contains(., 'bar')][@baz='xyz']">
<xsl:message terminate="yes">
Can't handle <foo> containing "bar" if @baz="xyz"
</xsl:message>
</xsl:key>
Я понимаю, что Saxon не прекратит работу, если я не использую ключ, например
<xsl:template match="/">
<xsl:apply-templates select="key('sanity-check', '')"/>
<xsl:copy-of select="."/>
</xsl:template>
Однако могу ли я быть уверен, что процессор XSLT на самом деле завершит работу с этим шаблоном? Я думаю, это не то, как ключи были разработаны для использования.
Фон: Менее привлекательные альтернативы (игнорировать, если tl; dr)
Я понимаю, что Schematron мог бы стать альтернативой этому подходу, но поскольку эти тесты могут быть не о достоверности документа, а о том, способна ли таблица стилей обрабатывать документ, я считаю тесты, которые реализованы самой таблицей стилей, весьма привлекательными.
Другой альтернативой может быть использование шаблонов вместо ключей. Я вижу два варианта здесь:
- Тестирующие и завершающие шаблоны отменяют "нормальные" шаблоны во время обычной обработки. Однако во время обычной обработки не все узлы, которые необходимо протестировать, обязательно обрабатываются с помощью сопоставления с шаблоном, которое может быть отменено.
Отдельный запуск тестирования с использованием шаблонов, например так:
<xsl:template match="/"> <xsl:apply-templates mode="sanity-check"/> <xsl:apply-templates mode="actual-processing"/> </xsl:template> <xsl:template match="node()|@*" mode="sanity-check"> <xsl:apply-templates select="node()|@*" mode="sanity-check"/> </xsl:template> <xsl:template mode="sanity-check" match="foo[contains(., 'bar')][@baz='xyz']"> <xsl:message terminate="yes"> foo containing bar can't have @baz set to xyz </xsl:message> </xsl:template>
Ключи все еще имеют преимущество, потому что более сложные проверки, которые не могут быть выражены простым шаблоном, возможны без дополнительных усилий. С шаблонами для этого потребуются такие конструкции:
<xsl:template mode="sanity-check" match="foo">
<xsl:variable name="variable" select="some-complicated-expression"/>
<xsl:if test="some-test-requiring[$variable=@bar]">
<xsl:message terminate="yes">
Fail!
</xsl:message>
</xsl:if>
<xsl:apply-templates mode="sanity-check" select="node()|@*"/>
</xsl:template>
Здесь есть два недостатка:
- Требуется дополнительный шаблонный код. Если мы допустим ошибку, тесты не будут выполнены правильно, но мы не заметим. Нам нужно:
- "преобразование идентичности" как шаблон
<xsl:apply-templates mode="sanity-check" select="node()|@*"/>
в каждом шаблоне, содержащем более сложные тесты.
- Проблемы с перекрывающимися тестами, например, с
match="A|B"
другой сmatch="B|C"
оба требуют некоторых<xsl:choose>
/<xsl:if>
решить, если нам нужно прекратить. Элемент<B>
будет соответствовать только одному из шаблонов. С ключами не нужно беспокоиться о совпадении совпадений.