Я использую.Net XslCompiledTranform для выполнения некоторого простого XSLT (см. ниже для упрощенного примера).
Пример, который XSLT предназначен, чтобы сделать просто, показывает значение параметра, который передается в шаблону. Вывод - то, чем я ожидаю, что это будет (т.е.
<result xmlns:p1="http://www.doesnotexist.com">
<valueOfParamA>valueA</valueOfParamA>
</result>
когда я использую сакса 9.0, но когда я использую XslCompiledTransform (XslTransform) в .net I, добираются
<result xmlns:p1="http://www.doesnotexist.com">
<valueOfParamA></valueOfParamA>
</result>
Проблема состоит в том, что это, значение параметра paramA не передается в шаблон, когда я использую классы .NET. Я полностью озадачил относительно почему. когда я ступаю через в Visual Studio, отладчик говорит, что шаблон назовут с paramA ='valueA', но когда выполнение переключится на шаблон, значение paramA является пробелом.
Кто-либо может объяснить, почему это происходит? Действительно ли это - ошибка в реализации MS, или (более вероятно) я делаю что-то, что запрещается в XSLT?
Любая справка значительно ценится.
Это - XSLT, который я использую
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:extfn="http://exslt.org/common" exclude-result-prefixes="extfn" xmlns:p1="http://www.doesnotexist.com">
<!--
Replace msxml with
xmlns:extfn="http://exslt.org/common"
xmlns:extfn="urn:schemas-microsoft-com:xslt"
-->
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="resultTreeFragment">
<p1:foo>
</p1:foo>
</xsl:variable>
<xsl:variable name="nodeset" select="extfn:node-set($resultTreeFragment)"/>
<result>
<xsl:apply-templates select="$nodeset" mode="AParticularMode">
<xsl:with-param name="paramA" select="'valueA'"/>
</xsl:apply-templates>
</result>
</xsl:template>
<xsl:template match="p1:foo" mode="AParticularMode">
<xsl:param name="paramA"/>
<valueOfParamA>
<xsl:value-of select="$paramA"/>
</valueOfParamA>
</xsl:template>
</xsl:stylesheet>
Нет ничего странного - это ожидаемое поведение любого процессора, совместимого с XSLT 1.0.
Объяснение :
Переменная $ nodeset
определена как:
в XSLT 1.0 содержит полный XML-документ - узел документа, обозначенный в XPath 1.0 как /
.
Следовательно,
<xsl:apply-templates select="$nodeset" mode="AParticularMode">
<xsl:with-param name="paramA" select="'valueA'"/>
</xsl:apply-templates>
применит шаблон, соответствующий дереву (узел документа /
) в указанном режиме, если такой шаблон существует. В вашем случае такого шаблона не существует. Поэтому применяется встроенный шаблон XSLT 1.0 для /
(который принадлежит каждому режиму ).
Текст встроенного шаблона можно найти в спецификации :
<xsl:template match="*|/">
<xsl:apply-templates/>
</xsl:template>
Согласно спецификации: " Существует также встроенное правило шаблона для каждый режим, который позволяет рекурсивной обработке продолжаться в том же режиме при отсутствии успешного сопоставления с шаблоном с помощью явного правила шаблона в таблице стилей. Это правило шаблона применяется как к узлам элемента, так и к корневому узлу. встроенное правило шаблона для режима m .
<xsl:template match="*|/" mode="m">
<xsl:apply-templates mode="m"/>
</xsl:template>
"
Конечно, встроенный шаблон ничего не знает о вашем параметре $ paramA
и не передает его вплоть до применяемых шаблонов.
Итак, наконец, для обработки выбирается ваш шаблон, соответствующий p1: foo "
в mode =" AParticularMode "
.В качестве значения параметра ничего не передается, поэтому он не имеет значения - таким образом,
не создает ни одного символа или узла.
Чтобы исправить эту проблему, просто добавьте сопоставление с шаблоном /
и в режиме «AParticularMode»
:
<xsl:template match="/" mode="AParticularMode">
<xsl:param name="paramA"/>
<xsl:apply-templates mode="AParticularMode">
<xsl:with-param name="paramA" select="$paramA"/>
</xsl:apply-templates>
</xsl:template>
, и теперь вы получите желаемый результат.
В XSLT 2.0 (Saxon 9) вы наблюдаете другое поведение , потому что встроенные шаблоны в XSLT 2.0 по определению повторно передают все параметры, с которыми они были применены - см. XSLT 2.0 Spec :
"Если встроенное правило было вызвано с параметрами, эти параметры передаются в неявной инструкции xsl: apply-templates."
После дополнительных экспериментов я обнаружил, что изменение шаблонов применения на
<xsl:apply-templates select="$nodeset/*" mode="AParticularMode">
<xsl:with-param name="paramA" select="'valueA'"/>
</xsl:apply-templates>
(обратите внимание на select = "$ nodeset / *"
вместо select = "nodeset"
) заставил его работать так, как я хотел, в .Net и Saxon.
Тем не менее, я был бы признателен, если бы кто-нибудь мог объяснить , почему моя первая попытка не удалась.
Ответ на ваш вопрос:
Почему моя первая попытка не удалась?
Поскольку вы удобно используете node-set () в своем коде, я думаю, вы, возможно, хорошо знаете о фрагменте дерева результатов. Если нет, то пройдите по этой ссылке .
Хорошо. Используя преимущество RTF [фрагмент дерева результатов], вы можете рассматривать «foo» как узел.
Переменная $ nodeset хранит древовидную структуру узла, так что вы можете рассматривать ее значение как набор узлов, тогда как переменная $ nodeset по-прежнему является переменной. Если вы хотите применить шаблон, затем примените его, его дочерние узлы [именно элементы] отображаются как его значение,
Вместо * вы могли бы использовать,
<xsl:apply-templates select="$nodeset/p1:foo" mode="AParticularMode">
Это более точно,
{{ 1}}