Суммирование вычисленных значений в XSLT

Вопрос: Я пытаюсь получить сумму всех цен товаров, умноженную на их количество, купленное, например (учитывая следующее)... 10 5 5 7 Я хочу получить общую сумму в 85 долларов. Я пробовал следующее, но вместо того, чтобы добавлять каждую покупку купленного элемента к общей переменной, она объединяет все значения в строку...

Вопрос:

Я пытаюсь получить сумму всех цен товаров, умноженную на их количество, купленное, например (учитывая следующее)…

<item> <itemPrice>10</itemPrice> <itemQty>5</itemQty> </item> <item> <itemPrice>5</itemPrice> <itemQty>7</itemQty> </item>

Я хочу получить общую сумму в 85 долларов. Я пробовал следующее, но вместо того, чтобы добавлять каждую покупку купленного элемента к общей переменной, она объединяет все значения в строку…

<xsl:template name=»itemsTotal»> <xsl:variable name=»total» select=»0″ /> <xsl:for-each select=»item»> <xsl:value-of select=»$total + (./itemQty * ./itemPrice)» /> </xsl:for-each> </xsl:template>

Какой лучший способ получить то, что я ищу? Могу ли я внести изменения в свой шаблон?

Лучший ответ:

Итерация решается рекурсией в XSLT 1.0.

<xsl:template name=»itemTotal»> <xsl:param name=»item» select=».» /> <xsl:param name=»carryOver» select=»0″ /> <xsl:variable name=»runningTotal» select=» $carryOver + $item/itemPrice * $item/itemQty » /> <xsl:variable name=»nextItem» select=»$item/following-sibling::item[1]» /> <xsl:choose> <xsl:when test=»not($nextItem)»> <xsl:value-of select=»$runningTotal» /> </xsl:when> <xsl:otherwise> <xsl:call-template name=»itemTotal»> <xsl:with-param name=»item» select=»$nextItem» /> <xsl:with-param name=»carryOver» select=»$runningTotal» /> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:template>

которую можно назвать так

<xsl:call-template name=»itemTotal»> <xsl:with-param name=»item» select=»root/item[1]» /> </xsl:call-template>

и выходы

85

Заметки

  • Это XSLT 1.0. Если ваш XSLT-процессор поддерживает XSLT 2.0 и XQuery, тогда вы можете решить это гораздо более элегантно с помощью выражения XQuery FLWOR.
  • Шаблон itemTotal является хвостовым рекурсивным, поэтому его можно оптимизировать в цикле XSLT-процессором. В этом случае переполнение стека не может произойти даже при высоких значениях <item>.
  • Шаблон в настоящее время зависит от того, являются ли братья и сестры (следовательно, использование following-sibling). Это работает сразу для вашего примера, но может потребоваться адаптировать его к другим входам.
  • Никаких функций расширения не требуется, но если ваш XSLT-процессор поддерживает их, вам может быть лучше использовать решение на основе node-set(), как показывает майкл-ответ.

Ответ №1

Предполагая, что вы используете XSLT 1.0 и считаете, что ваш процессор поддерживает функцию node-set() EXSLT (что почти наверняка) и предполагая, что мы добавим корневой элемент в ваш пример ввода XML и предположим, что вы действительно хотите, чтобы результат был 10 * 5 + 5 * 7, который равен 85, а не 80, попробуйте следующую таблицу стилей:

<xsl:stylesheet version=»1.0″ xmlns:xsl=»http://www.w3.org/1999/XSL/Transform» xmlns:exsl=»http://exslt.org/common» extension-element-prefixes=»exsl»> <xsl:output method=»xml» version=»1.0″ encoding=»utf-8″ indent=»yes»/> <xsl:template match=»/»> <!— … —> <xsl:variable name=»extPrices»> <xsl:for-each select=»items/item»> <extPrice> <xsl:value-of select=»itemPrice*itemQty» /> </extPrice> </xsl:for-each> </xsl:variable> <xsl:variable name=»subTotal» select=»sum(exsl:node-set($extPrices)/extPrice)» /> <subTotal><xsl:value-of select=»$subTotal» /></subTotal> <!— … —> </xsl:template> </xsl:stylesheet>

Применяется для исправленного ввода:

<items> <item> <itemPrice>10</itemPrice> <itemQty>5</itemQty> </item> <item> <itemPrice>5</itemPrice> <itemQty>7</itemQty> </item> </items>

результат:

<?xml version=»1.0″ encoding=»utf-8″?> <subTotal>85</subTotal>

Оцените статью
Добавить комментарий