Строительный список с xslt - PullRequest
       12

Строительный список с xslt

2 голосов
/ 09 апреля 2009

Я пытаюсь создать список, который анализирует весь мой XML-документ. Мне нужно перечислить числовые имена, а затем альфа-имена. Список должен выглядеть примерно так.

6
6600 Training
6500 Training

A
Accelerated Training

T
Training

Это фрагмент XML.

<courses>
    <course>         
        <name>Accelerated Training</name>
    </course>
    <course>        
        <name>6600 Training</name>
    </course>   
         <course>        
        <name>Training</name>
    </course>
    <course>        
        <name>6500 Training</name>
    </course>   

</courses>   

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

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

  <xsl:output omit-xml-declaration="yes" indent="yes"/> 
  <xsl:variable name="vLower" select= "'abcdefghijklmnopqrstuvwxyz'"/> 
  <xsl:variable name="vUpper" select= "'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/> 

  <xsl:key name="kTitleBy1stLetter" match="courses/course"  use="substring(name,1,1)"/>    

  <xsl:template match="/*">      

    <xsl:for-each select="course [generate-id() = generate-id(key('kTitleBy1stLetter', substring(name,1,1)) [1] ) ]">        
      <xsl:variable name="v1st" select="substring(name,1,1)"/>        
      <h2><xsl:value-of select="$v1st"/></h2>        
      <div class="{translate($v1st, $vUpper, $vLower)}-content">
        <ul>
          <xsl:for-each select="key('kTitleBy1stLetter',$v1st)">               
            <li><xsl:value-of select="name"/></li>
          </xsl:for-each>          
        </ul>      
      </div>      
    </xsl:for-each>        
  </xsl:template>
</xsl:stylesheet>

Ответы [ 3 ]

7 голосов
/ 09 апреля 2009

В основном вам нужно сгруппировать по первой букве и отсортировать по <name>. Вы уже хорошо справляетесь с подходом мюнхенской группировки.

Я бы предложил альтернативу, которая немного проще для глаз:

<xsl:key name="kInitial" match="course" use="substring(name, 1, 1)" />

<xsl:template match="courses">
  <xsl:apply-templates select="course" mode="initial">
    <xsl:sort select="name" />
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="course" mode="initial">
  <xsl:variable name="initial" select="substring(name, 1, 1)" />
  <xsl:variable name="courses" select="key('kInitial', $initial)" />
  <xsl:if test="generate-id() = generate-id($courses[1])">
    <h2><xsl:value-of select="$initial"/></h2>
    <ul>
      <xsl:apply-templates select="$courses">
        <xsl:sort select="name" />
      </xsl:apply-templates>
    </ul>
  </xsl:if>
</xsl:template>

<xsl:template match="course">
  <li>
    <xsl:value-of select="name"/>
  </li>
</xsl:template>

выходы:

<h2>6</h2>
<ul>
  <li>6500 Training</li>
  <li>6600 Training</li>
</ul>
<h2>A</h2>
<ul>
  <li>Accelerated Training</li>
</ul>
<h2>T</h2>
<ul>
  <li>Training</li>
</ul>

РЕДАКТИРОВАТЬ: Ради разборчивости я пропустил верхний регистр первой буквы. Правильный ключ будет таким (вы не можете использовать переменную в ключе, следовательно, строки буквального алфавита):

<xsl:key name="kInitial" match="course" use="
  translate(
    substring(name, 1, 1), 
    'abcdefghijklmnopqrstuvwxyz', 
    'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  )
" />

То же самое относится и к переменной $initial во втором шаблоне, но здесь вы можете использовать переменные снова.

РЕДАКТИРОВАТЬ # 2: Поскольку сортировка также чувствительна к регистру, вы можете использовать то же выражение:

<xsl:sort select="translate(substring(name, 1, 1), $vLower, $vUpper)" />
2 голосов
/ 09 апреля 2009

Решение XSLT 2.0 :

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

    <xsl:template match="/*">
      <xsl:for-each-group select="course"
           group-by="upper-case(substring(name,1,1))">
        <xsl:sort select="current-grouping-key()"/>

        <xsl:sequence select=
           "concat('&#xA;', current-grouping-key())"/>

        <xsl:for-each select="current-group()">
          <xsl:sort select="upper-case(name)"/>
          <xsl:sequence select="concat('&#xA;&#x9;', name)"/>
        </xsl:for-each>
      </xsl:for-each-group>
    </xsl:template>
</xsl:stylesheet>

Когда вышеуказанное преобразование применяется к первоначально предоставленному документу XML :

<courses>
    <course>
        <name>Accelerated Training</name>
    </course>
    <course>
        <name>6600 Training</name>
    </course>
    <course>
        <name>Training</name>
    </course>
    <course>
        <name>6500 Training</name>
    </course>
</courses>

желаемый результат получается (в текстовом формате для простоты - создание HTML оставлено в качестве упражнения для читателя:)

6 
    6500 Training 
    6600 Training 
A 
    Accelerated Training 
T 
    Training

Примечание :

  1. Использование инструкции <xsl:for-each-group> XSLT 2.0

  2. Использование функций current-grouping-key() и current-group() XSLT 2.0.

  3. Использование функции upper-case() XPath 2.0

0 голосов
/ 09 апреля 2009

Что ж, числовая часть сложна, если вам нужно что-то сложное, но, основываясь на вашем идеальном выводе, все, что вам не хватает, - это простая сортировка на вашем для каждого:

<xsl:sort select="key('kTitleBy1stLetter', substring(name,1,1))" />

Предостережение: я не утверждаю, что это лучший или единственный или иным образом метод, только то, что он работает на полную мощность и использует то, что у вас уже есть.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...