XSL преобразование - PullRequest
       6

XSL преобразование

5 голосов
/ 26 августа 2010

Я новичок в преобразованиях xsl и у меня есть вопрос. Я зацикливаюсь на XML, как это:

 <PO>
<Items>
  <Item>
     <Price>2</Price>
     <Quantity>5</Quantity>
  </Item>
  <Item>
     <Price>3</Price>
     <Quantity>2</Quantity>
  </Item>    
 </Items>
 <QuantityTotal></QuantityTotal>
 </PO>

Теперь я хочу вставить значение в узел QuantityTotal:
Значение представляет собой сумму цены * количество всех предметов, в этом случае (2 * 5) + (3 * 2) = 16 Как я могу это сделать, я попробовал это с циклом и переменными, но переменные неизменны, поэтому я не знаю, как я могу этого достичь.

Спасибо за помощь

Ответы [ 3 ]

4 голосов
/ 27 августа 2010

Вот решение XSLT - функции расширения не требуются :

<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="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="QuantityTotal">
  <xsl:copy>
   <xsl:call-template name="sumProducts">
     <xsl:with-param name="pNodes" select="../Items/Item"/>
   </xsl:call-template>
  </xsl:copy>
 </xsl:template>

 <xsl:template name="sumProducts">
  <xsl:param name="pNodes"/>
  <xsl:param name="pSum" select="0"/>
  <xsl:param name="pEname1" select="'Price'"/>
  <xsl:param name="pEname2" select="'Quantity'"/>

  <xsl:choose>
   <xsl:when test="not($pNodes)">
    <xsl:value-of select="$pSum"/>
   </xsl:when>
  <xsl:otherwise>
    <xsl:call-template name="sumProducts">
      <xsl:with-param name="pNodes" select=
      "$pNodes[position() > 1]"/>
      <xsl:with-param name="pSum" select=
      "$pSum
      +
       $pNodes[1]/*[name()=$pEname1]
      *
       $pNodes[1]/*[name()=$pEname2]
       "/>
    </xsl:call-template>
  </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

, когда это преобразование применяется к предоставленному документу XML :

<PO>
    <Items>
        <Item>
            <Price>2</Price>
            <Quantity>5</Quantity>
        </Item>
        <Item>
            <Price>3</Price>
            <Quantity>2</Quantity>
        </Item>
    </Items>
    <QuantityTotal></QuantityTotal>
</PO>

желаемый результат получается :

<PO>
   <Items>
      <Item>
         <Price>2</Price>
         <Quantity>5</Quantity>
      </Item>
      <Item>
         <Price>3</Price>
         <Quantity>2</Quantity>
      </Item>
   </Items>
   <QuantityTotal>16</QuantityTotal>
</PO>
2 голосов
/ 28 августа 2010

Помимо превосходного ответа Димитра, эта таблица стилей использует другой подход:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="QuantityTotal">
        <xsl:copy>
            <xsl:apply-templates select="../Items/Item[1]" mode="sum"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="Item" mode="sum">
        <xsl:param name="pSum" select="0"/>
        <xsl:variable name="vNext" select="following-sibling::Item[1]"/>
        <xsl:variable name="vSum" select="$pSum + Price * Quantity"/>
        <xsl:apply-templates select="$vNext" mode="sum">
            <xsl:with-param name="pSum" select="$vSum"/>
        </xsl:apply-templates>
        <xsl:if test="not($vNext)">
            <xsl:value-of select="$vSum"/>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

Выход:

<PO>
    <Items>
        <Item>
            <Price>2</Price>
            <Quantity>5</Quantity>
        </Item>
        <Item>
            <Price>3</Price>
            <Quantity>2</Quantity>
        </Item>
    </Items>
    <QuantityTotal>16</QuantityTotal>
</PO>
1 голос
/ 27 августа 2010

Вот решение, использующее XSLT2, в котором наборы узлов являются объектами первого класса.В XSLT1 вам необходимо использовать расширение набора узлов.

Объяснение ниже:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:variable name="extendedItems" as="xs:integer*">
        <xsl:for-each select="//Item">
             <xsl:value-of select="./Price * ./Quantity"/>
        </xsl:for-each>
    </xsl:variable>

    <xsl:variable name="total">
        <xsl:value-of select="sum($extendedItems)"/>
    </xsl:variable>

    <xsl:template match="//QuantityTotal">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:value-of select="$total"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

Подход здесь заключается в использовании «преобразования идентичности» для копирования документа во время выполнениярасчеты и вставка результата в выходной шаблон QuantityTotal.Первый шаблон копирует входные данные в выходные данные, но переопределяется более конкретным шаблоном для QuantityTotal внизу.Объявление первой переменной создает список расширенных затрат, а определение второй переменной суммирует затраты для получения итоговой суммы.Затем итоговое значение вставляется в узел QuantityTotal.

Ключом к пониманию XSL является его декларативный характер.Наиболее распространенная концептуальная ошибка, допущенная почти всеми новичками, заключается в предположении, что таблица стилей является последовательной программой, которая обрабатывает входной XML-документ.На самом деле все наоборот.Механизм XSL читает документ XML.и для каждого нового тега он ищет в таблице стилей «лучшее» соответствие, выполняя этот шаблон.

РЕДАКТИРОВАНИЕ:

Вот версия xslt1.1, которая работает с Saxon 6.5

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:ex="http://exslt.org/common"
    extension-element-prefixes="ex"
    version="1.1">
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:variable name="extendedItems">
        <xsl:for-each select="//Item">
            <extended>
             <xsl:value-of select="./Price * ./Quantity"/>
            </extended>
            </xsl:for-each>
    </xsl:variable>
    <xsl:variable name="total">
        <xsl:value-of select="sum(ex:node-set($extendedItems/extended))"/>
    </xsl:variable>
    <xsl:template match="//QuantityTotal">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:value-of select="$total"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...