2014-09-02 3 views
0

похоже на эту ссылку xsl sum siblings based on node value, мне нужно применить преобразование xsl, которое суммирует определенные значения узлов на основе значения одного из его узлов-сестер, проблема в том, что есть похожие узлы, которые Я хочу получить сумму, но я не смог понять xslt из-за разных родителей. вот как выглядит xml.xsl sum братьев и сестер с разными родителями на основе значения узла

<?xml version="1.0" encoding="ISO-8859-1" ?> 
<Message> 
<Body> 
    <Order> 
     <Item1> 
      <.../> 
      <Type>widget</Type> 
      <Qty>20</Qty> 
      <.../> 
     </Item1> 
     <Item> 
      <.../> 
      <Type>gadget</Type> 
      <Qty>10</Qty> 
      <.../> 
     </Item> 
     <Item2> 
      <.../> 
      <Type>widget</Type> 
      <Qty>5</Qty> 
      <.../> 
     </Item2> 
     <Item3> 
      <.../> 
      <Type>widget</Type> 
      <Qty>2</Qty> 
      <.../> 
     </Item3> 
     <Item3> 
      <.../> 
      <Type>Other</Type> 
      <Qty>0</Qty> 
      <.../> 
     </Item3> 
     <Item/> 
    </Order> 
    </Body> 
</Message> 

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

<xsl:value-of select="sum(/Message/Body/Order/Item1|Item2|Item3[Type = 'widget'] /Qty)"/> 

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

Widget Всего = 27
Gadget Всего = 10

Я также хотел бы, чтобы исключить в том числе типа с суммой, равной 0 от выхода.

Спасибо! Raf

ответ

0

Проблема с текущим выражения правила приоритета оператора - ваш XPath из

/Message/Body/Order/Item1|Item2|Item3[Type = 'widget']/Qty 

разбирает, как

(/Message/Body/Order/Item1) 
| 
(Item2) 
| 
(Item3[Type = 'widget']/Qty) 

С вы отметили вопрос как XSLT-2.0, вы можете использовать круглые скобки:

/Message/Body/Order/(Item1|Item2|Item3)[Type = 'widget']/Qty 

Или подстановочные, если вы не знаете, все возможные имена авансовые

/Message/Body/Order/*[Type = 'widget']/Qty 

Что касается более общего вопроса, это выглядит как хорошая работа для. for-each-group

<xsl:for-each-group select="/Message/Body/Order/*" group-by="Type"> 
    <xsl:variable name="totalQty" select="sum(current-group()/Qty)"/> 
    <xsl:if test="$totalQty gt 0"> 
    <item type="{current-grouping-key()}" qty="{$totalQty}"/> 
    </xsl:if> 
</xsl:for-each-group> 
+0

Привет, Ян, есть способ не жестко кодировать типы, которые я получаю? например, я не хочу создавать путь для каждого типа, например/Message/Body/Order/(Item1 | Item2 | Item3) [Type = 'widget']/Qty,/Message/Body/Order/(Item1 | Item2 | Item3) [Type = 'gadget']/Qty, я хотел, чтобы он был динамическим. Благодаря! – raf

+0

@raf, поскольку вы работаете в XSLT 2.0, вам повезло - это легко с 'for-each-group' –

0

попытаться иметь эти вместо:

<xsl:value-of select="sum(/Message/Body/Order//Qty[preceding-sibling::Type[.='widget']])"/> 

<xsl:value-of select="sum(/Message/Body/Order//Qty[preceding-sibling::Type[.='gadget']])"/> 

приводит 27 и 10 соответственно.

EDIT

В XSLT-2.0, вы можете использовать xsl:for-each-group ниже

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    version="2.0"> 

    <xsl:strip-space elements="*"/> 

    <xsl:output indent="yes" omit-xml-declaration="yes"/> 

    <xsl:template match="/"> 
     <xsl:for-each-group select="/Message/Body/Order/*" group-by="Type"> 
      <xsl:value-of select="concat(current-grouping-key(), ': ')"/> 
      <xsl:copy-of select="sum(current-group()/Qty)"/> 
      <xsl:text>&#xA;</xsl:text> 
     </xsl:for-each-group> 
    </xsl:template> 


</xsl:stylesheet> 

выводит:

widget: 27 
gadget: 10 
Other: 0 
+0

Hi! Спасибо за ваш ответ. Извините, я не очень понял свой вопрос. есть способ не жестко кодировать тип в xpath? – raf

+0

вам нужно использовать ключи, как ответил Тим С. –

+0

Я отредактировал свой ответ. Пожалуйста, проверьте. –

0

Вы могли бы рассмотреть вопрос об использовании key здесь, чтобы посмотреть вверх Предметы.Если предположить, что элементы, которые вы хотите, чтобы сумма всегда дети order элементов, они ключ будет выглядеть следующим образом:

<xsl:key name="item" match="Order/*" use="Type" /> 

Затем подвести итоги «виджетов» пунктов, вы могли бы сделать это:

<xsl:value-of select="sum(key('item', 'widget')/Qty)" /> 

и если вы хотите, чтобы исключить суммы в общей сложности 0 от выхода, просто использовать переменную, и убедитесь, что

<xsl:variable name="widget" select="sum(key('item', 'widget')/Qty)" /> 
<xsl:if test="$widget > 0"> 
    <xsl:text>Widget Total = </xsl:text> 
    <xsl:value-of select="$widget" /> 
</xsl:if> 

и если вы хотите, чтобы избежать жесткого кодирования, один путь к и се xsl:for-each-group, чтобы получить различные предметы, например:

<xsl:for-each-group select="//Order/*" group-by="Type"> 
    <xsl:variable name="value" select="sum(key('item', Type)/Qty)" /> 
    <xsl:if test="$value > 0"> 
     <xsl:value-of select="Type" /> 
     <xsl:text> Total = </xsl:text> 
     <xsl:value-of select="$value" /> 
     <xsl:value-of select="'&#10;'" /> 
    </xsl:if> 
    </xsl:for-each-group> 
+0

Спасибо за ваш отзыв Тим !, сумма также может иметь отрицательное значение, но я получаю эту часть. Мой xml может также иметь Body1 и Body2, но виджет, гаджет и другие могут также измениться на другой тип, есть ли способ не скомпрометировать их? Извините, если этого нет в исходном вопросе, я обновлю свой пост. Благодаря! – raf

+0

Я отредактировал свой ответ, чтобы показать возможный путь. Хотя я вижу, что Ян Робертс сделал то же самое, но лучше (используя «текущую группу», которая устраняет необходимость в ключе). –

Смежные вопросы