Параметры, не передаваемые в шаблон при использовании классов преобразования .Net - PullRequest
5 голосов
/ 08 марта 2010

Я использую .Net XslCompiledTranform для запуска некоторого простого XSLT (см. Упрощенный пример ниже).

Пример XSLT предназначен для простого отображения значения параметра, который передается в шаблон. Результат - это то, что я ожидаю (т. Е.

<result xmlns:p1="http://www.doesnotexist.com">
  <valueOfParamA>valueA</valueOfParamA>
</result>

когда я использую Saxon 9.0, но когда я использую XslCompiledTransform (XslTransform) в .net я получаю

<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>

Ответы [ 3 ]

4 голосов
/ 09 марта 2010

В этом нет ничего странного - это ожидаемое поведение любого XSLT 1.0-совместимого процессора.

Объяснение : Переменная $nodeset определена как:

<xsl:variable name="nodeset" select="extfn:node-set($resultTreeFragment)"/>

в 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 для / (который относится к каждому режиму ).

Текст встроенного шаблона можно найти в spec :

<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", выбран для обработки. Ничто не передается в качестве значения для параметра, поэтому оно не имеет значения - таким образом, <xsl:value-of> не производит ни одного символа или узла.

Чтобы исправить эту проблему, просто добавьте шаблон, соответствующий /, и в режиме "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. "

0 голосов
/ 09 марта 2010

Ответ на свой вопрос,

Почему моя первая попытка не удалась?

Поскольку вы с комфортом используете node-set () в своем коде, я думаю, вы, возможно, хорошо осведомлены о Result Tree Fragment. Если нет, то перейдите по этой ссылке .

Хорошо. Воспользовавшись RTF [Фрагмент дерева результатов], вы можете рассматривать «foo» как узел.

Переменная $ nodeset хранит древовидную структуру узла, так что вы можете рассматривать ее значение как набор узлов, где переменная $ nodeset по-прежнему является переменной. Если вы хотите применить -template затем примениться, это дочерние узлы [именно элементы], которые отображаются как его значение,

Вместо * вы могли бы использовать

<xsl:apply-templates select="$nodeset/p1:foo" mode="AParticularMode">

Это точнее,

0 голосов
/ 08 марта 2010

Ну что ж, после дальнейших экспериментов я обнаружил, что изменение 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.

Я все же был бы благодарен, если бы кто-нибудь смог объяснить , почему моя первая попытка не удалась.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...