Присвоение узла переменной xsl: с помощью зависимости от теста - PullRequest
1 голос
/ 30 марта 2012

Я совершенно незнаком с xslt, поэтому извините, если это глупый вопрос. Мне нужно объявить переменную и указать ее на один из 2-х возможных узлов в XML, в зависимости от того, какой из них действительно существует. Я пытаюсь следующее:

<xsl:variable name="DealNode">
    <xsl:choose>
        <xsl:when test="/AllResponse/Deals/Deal"><xsl:copy-of select="/AllResponse/Deals/Deal"/></xsl:when>
        <xsl:otherwise><xsl:copy-of select="/AllResponse/BookDeals/BookDeal"/></xsl:otherwise>
    </xsl:choose>
</xsl:variable>

Похоже, это работает, поскольку DealNode, похоже, соответствует ожиданиям. Однако, если я теперь сделаю:

<xsl:variable name="TradeNode" select="$DealNode/Trades/Trade"/>

TradeNode остается пустым. Что я делаю не так?

Пример xml:

<AllResponse>
    <Deals>
        <Deal>
            <Trades>
                <Trade>
                </Trade>
            </Trades>
        </Deal>
    </Deals>
</AllResponse>

Ответы [ 3 ]

3 голосов
/ 31 марта 2012

В настоящее время принятый ответ имеет серьезную проблему. Если его выражение XPath оценивается по следующему документу XML :

<AllResponse>
    <BookDeals>
        <BookDeal>
            <Trades>
                <Trade>
                </Trade>
            </Trades>
        </BookDeal>
    </BookDeals>
    <Deals>
        <Deal>
            <Trades>
                <Trade>
                </Trade>
            </Trades>
        </Deal>
    </Deals>
</AllResponse>

тогда, вопреки утверждениям в ответе, предоставляется XPath-выражение :

(/AllResponse/Deals/Deal | /AllResponse/BookDeals/BookDeal)[1]

не выбирает первый аргумент объединения, но в точности противоположен (2-й аргумент).

Причина этого заключается в том, что результат операции объединения всегда сортируется по порядку документа его узлов - другими словами, набор узлов - это набор, а не последовательность.

Вот правильное решение :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="/">
     <xsl:variable name="vDealNode" select=
      "/*/Deals/Deal | /*[not(Deals/Deal)]/BookDeals/BookDeal"/>

     <xsl:copy-of select="$vDealNode"/>
 </xsl:template>
</xsl:stylesheet>

когда это преобразование применяется к вышеуказанному XML-документу, выбирается нужный, правильный результат (узел, который является первым аргументом оператора объединения) , и результат этого выбора выводится:

<Deal>
   <Trades>
      <Trade/>
   </Trades>
</Deal>

Объяснение : правильное общее выражение, которое нужно использовать, когда мы хотим выбрать $ns1, когда условие $cond равно true(), и выбрать $ns2, если $cond равно false, это:

$ns1[$cond] | $ns2[not($cond)]

В нашем конкретном случае:

$ns1 - это /*/Deals/Deal,

$ns2 is /*/BookDeals/BookDeal

и

$cond is boolean(/*/Deals/Deal)

Подставляя их в общее выражение выше и сокращая :

/*/Deals/Deal[/*/Deals/Deal]

до:

/*/Deals/Deal

мы приходим к выражению, используемому в этом ответе :

/*/Deals/Deal | /*[not(Deals/Deal)]/BookDeals/BookDeal
2 голосов
/ 30 марта 2012

Один из подходов к определению переменной заключается в следующем:

<xsl:variable name="DealNode" select="(/AllResponse/Deals/Deal | /AllResponse/BookDeals/BookDeal)[1]"/>

Это формирует объединение обоих выбранных наборов узлов и принимает первый узел в этом объединении таким образом, если первое выражение выбирает узелэтот узел берется, если первое выражение ничего не выбирает, тогда берется первый узел, выбранный вторым выражением.

0 голосов
/ 30 марта 2012

XSLT1 не полностью поддерживает фрагменты дерева результатов. Чтобы сделать то, что вы пытаетесь, требуется exsl:node-set().

Кроме того, даже в XSLT2 правильный XPath равен $DealNode/Deal/Trades/Trade

...