XSLT: группировка и сортировка .... как? - PullRequest
3 голосов
/ 24 сентября 2010

У меня есть XML-файл, который выглядит следующим образом ...

    <states>
 <state>
  <name>North Carolina</name>
  <city>Charlotte</city>
 </state>
 <state>
  <name>Alaska</name>
  <city>Fairbanks</city>
 </state>
 <state>
  <name>Virginia</name>
  <city>Leesburg</city>
 </state>
 <state>
  <name>Alaska</name>
  <city>Coldfoot</city>
 </state>
 <state>
  <name>North Carolina</name>
  <city>Harrisburg</city>
 </state>
 <state>
  <name>Virginia</name>
  <city>Ashburn</city>
 </state>  
</states>

Мне нужно создать отчет, в котором перечислены все штаты, в алфавитном порядке с каждым городом, следующим ...., например..

Alaska - Fairbanks, Coldfoot
North Carolina - Charlotte, Harrisburg
Virginia - Leesburg, Ashburn

(города не должны быть в альфа-порядке, только штаты)

Я попытался решить эту проблему, выполнив для каждого состояния / состояния, отсортировав ихпо имени и обработке его.Вот так ....

    <xsl:for-each select="states/state">
       <xsl:sort select="name" data-type="text" order="ascending"/>
       <xsl:value-of select="name"/>-<xsl:value-of select="city"/>
    </xsl:for-each>   

Это дало мне ....

 Alaska - Fairbanks
 Alaska - Coldfoot
 North Carolina - Charlotte
 North Carolina - Harrisburg
 Virginia - Leesburg
 Virginia - Ashburn

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

<xsl:for-each select="states/state">
             <xsl:sort select="name" data-type="text" order="ascending"/>
  <xsl:variable name="name"><xsl:value-of select="name">
  <xsl:variable name="previous-name"><xsl:value-of select="(preceding-sibling::state)/name">
  <xsl:if test="$name != $previous-name">
   <br/><xsl:value-of select="name"/>-
  </xsl:if>
  <xsl:value-of select="city"/>
 </xsl:for-each>

К сожалению, похоже, что функция предыдущего брата не очень хорошо работает с сортировкой, поэтому в первый раз (на первой Аляске) она увидела первую Северную Каролинукак предыдущий брат.Это вызывает некоторые странные результаты, которые мне совсем не понравились.

Итак, я использую XSLT1.0 ... Есть мысли / предложения?

Спасибо

Ответы [ 3 ]

5 голосов
/ 24 сентября 2010

Эта таблица стилей:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="kStateByName" match="state" use="name"/>
    <xsl:output method="text"/>
    <xsl:template match="/">
        <xsl:apply-templates
                   select="/*/state[count(.|key('kStateByName',name)[1])=1]">
            <xsl:sort select="name"/>
        </xsl:apply-templates>
    </xsl:template>
    <xsl:template match="state">
        <xsl:value-of select="concat(name,' - ')"/>
        <xsl:apply-templates select="key('kStateByName',name)/city"/>
    </xsl:template>
    <xsl:template match="city">
        <xsl:value-of select="concat(.,substring(', ', 
                                                 1 div (position()!=last())),
                                       substring('&#xA;',
                                                 1 div (position()=last())))"/>
    </xsl:template>
</xsl:stylesheet>

Выход:

Alaska - Fairbanks, Coldfoot
North Carolina - Charlotte, Harrisburg
Virginia - Leesburg, Ashburn

Примечание : группировка по названию штата. Выражение подстроки разделителя работает только в стиле pull (применение шаблонов к городу)

Решение XSLT 2.0:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:template match="states">
        <xsl:for-each-group select="state" group-by="name">
            <xsl:sort select="name"/>
            <xsl:value-of select="concat(name,
                                         ' - ',
                                         string-join(current-group()/city,', '),
                                         '&#xA;')"/>
        </xsl:for-each-group>
    </xsl:template>
</xsl:stylesheet>

Просто для удовольствия, это выражение XPath 2.0:

string-join(for $state in distinct-values(/*/*/name)
            return concat($state,
                          ' - ',
                          string-join(/*/*[name=$state]/city,
                                      ', ')),
            '&#xA;')
1 голос
/ 24 сентября 2010

Для группировки в XSLT 1.0 вам, вероятно, придется использовать метод Мюнхена .Это может быть трудно понять, но как только вы заработаете, вам будет хорошо.

0 голосов
/ 24 сентября 2010

Это вернет отдельный список состояний:

  <xsl:for-each select="states/state">
     <xsl:sort select="name" />
     <xsl:if test="not(name = preceding-sibling::state/name)" >
         <xsl:value-of select="name" />
     </xsl:if>
  </xsl:for-each>

Я использовал ваш пример XML, создал небольшую таблицу стилей с вышеприведенным, провел через Xalan-j, и он возвращает:

Alaska Северная Каролина Virginia

Таким образом, вы сможете применить шаблон или другой цикл for-each для извлечения списка городов для каждого отдельного штата.

Chris

...