2013-04-04 4 views
1

У меня есть файл XML, как этотXSL Группировка по вложенному параметру

<fruits> 
    <fruit> 
    <name>banana</name> 
    <country>Morocco</country> 
    </fruit> 
    <fruit> 
    <name>orange</name> 
    <country>Morocco</country> 
    </fruit> 
    <fruit> 
    <name>grape</name> 
    <country>Egypt</country> 
    </fruit> 
</fruits> 

И мне нужно сгруппировать по-другому:

<fruits> 
    <country name="Morocco"> 
    <fruit> 
     <name>banana</name> 
    </fruit> 
    <fruit> 
     <name>orange</name> 
    </fruit> 
    </country> 
    <country name="Egypt"> 
    <fruit> 
     <name>grape</name> 
    </fruit> 
    </country> 
</fruits> 

Я пытался сделать это с for-each-group из XSLT 2.0, но это было плохо: я не знаю, как обрабатывать группировку по вложенным параметрам, поэтому мой .xsl-файл не делает ничего хорошего.

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

    <xsl:output method="xml" indent="yes"/> 

    <xsl:for-each-group select="fruits/fruit" group-by="fruits/fruit/country"> 
    <country name="{country}"> 
     <xsl:for-each select="current-group()"> 
     <fruit> 
      <name> '{name}'/</name> 
     </fruit> 
     </xsl:for-each> 
    </country> 
    </xsl:for-each-group> 

</xsl:stylesheet>  
+0

Вы близки - 'группы by' выражение является XPath относительно' select'ed узлов, так что вам нужно только 'группы по =«страна»' –

ответ

1

Как об этом:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"> 
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/> 

    <xsl:template match="/*"> 
    <xsl:copy> 
     <xsl:for-each-group select="fruit" group-by="country"> 
     <country name="{country}"> 
      <xsl:for-each select="current-group()"> 
      <fruit> 
       <name> 
       <xsl:value-of select="name" /> 
       </name> 
      </fruit> 
      </xsl:for-each> 
     </country> 
     </xsl:for-each-group> 
    </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

или несколько более чистого подхода:

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

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

    <xsl:template match="/*"> 
    <xsl:copy> 
     <xsl:for-each-group select="fruit" group-by="country"> 
     <country name="{country}"> 
      <xsl:apply-templates select="current-group()" /> 
     </country> 
     </xsl:for-each-group> 
    </xsl:copy> 
    </xsl:template> 
    <xsl:template match="fruit/country" /> 

</xsl:stylesheet> 

Либо один, при запуске на своем входе образца, производит:

<fruits> 
    <country name="Morocco"> 
     <fruit> 
     <name>banana</name> 
     </fruit> 
     <fruit> 
     <name>orange</name> 
     </fruit> 
    </country> 
    <country name="Egypt"> 
     <fruit> 
     <name>grape</name> 
     </fruit> 
    </country> 
</fruits> 
1

Если вы ограничены XSLT 1.0, тогда существует fe w способы сделать это: ни один из них не аккуратный. Этот ищет все элементы <fruit>, у которых нет предыдущих братьев и сестер с той же страной. Затем он копирует элемент <country>, а затем новый узел <fruit> для себя и каждого следующего брата с той же страной.

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

    <xsl:output indent="yes"/> 

    <xsl:template match="/fruits"> 
    <xsl:copy> 
     <xsl:apply-templates select="*"/> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="fruit"> 
    <xsl:if test="not(country = preceding-sibling::fruit/country)"> 
     <country> 
     <xsl:attribute name="name"> 
      <xsl:value-of select="country"/> 
     </xsl:attribute> 
     <xsl:for-each select="../fruit[country=current()/country]"> 
      <fruit> 
      <xsl:copy-of select="name" /> 
      </fruit> 
     </xsl:for-each> 
     </country> 
    </xsl:if> 
    </xsl:template> 

</xsl:stylesheet> 

выход

<?xml version="1.0" encoding="utf-8"?> 
<fruits> 
    <country name="Morocco"> 
     <fruit> 
     <name>banana</name> 
     </fruit> 
     <fruit> 
     <name>orange</name> 
     </fruit> 
    </country> 
    <country name="Egypt"> 
     <fruit> 
     <name>grape</name> 
     </fruit> 
    </country> 
</fruits> 

Muenchian Метод ускоряет этот запрос при помощи key средство XSLT, и может быть полезен при значительных наборах данных. Это альтернативное решение объявляет ключ fruit-by-country, так что все элементы <fruit> с тем же значением для элемента <country> могут быть выбраны, используя, скажем, key('fruit-by-country', 'Morocco'). Шаблон использует ключ для проверки того, является ли текущий <fruit> первым с этим значением для <country>, а также для выбора всех фруктов в одной группе, чтобы они могли отображаться вместе. Выходной сигнал идентичен выходу предыдущего преобразования.

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

    <xsl:output indent="yes"/> 

    <xsl:key name="fruit-by-country" match="fruit" use="country" /> 

    <xsl:template match="/fruits"> 
    <xsl:copy> 
     <xsl:apply-templates select="*"/> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="fruit"> 
    <xsl:if test="generate-id() = generate-id(key('fruit-by-country', country)[1])"> 
     <country> 
     <xsl:attribute name="name"> 
      <xsl:value-of select="country"/> 
     </xsl:attribute> 
     <xsl:for-each select="key('fruit-by-country', country)"> 
      <fruit> 
      <xsl:copy-of select="name" /> 
      </fruit> 
     </xsl:for-each> 
     </country> 
    </xsl:if> 
    </xsl:template> 

</xsl:stylesheet> 
+0

Даже в XSLT 1.0, Группование Muenchian гораздо предпочтительнее группировки с «предшествующим браком». – JLRishe

+0

@JLRishe: Я согласен, что он более эффективен, но он также (еще больше) непрозрачен и непонятен. Так что это зависит от того, что вы подразумеваете под * предпочтительным *. Вы высказали свою точку зрения, но нижний план говорит, что мой ответ * не полезен *. Вы действительно готовы сказать об этом? – Borodin

+0

Это просто мое мнение, но я рассматриваю группировку с «предшествующим браком» как плохую практику, даже если ее немного легче понять. Ваш ответ также неверен. Пожалуйста, внимательно сравните свой выход с желаемым выходом OP. – JLRishe

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