开发者

Alphabetize two elements in the same list with XSLT

开发者 https://www.devze.com 2023-03-31 18:07 出处:网络
I am very new to XSLT so please bear with me if my coding is sloppy or unnecessarily complicated. I am trying to create a list of songs that among other things needs to be alphabetized by title. For s

I am very new to XSLT so please bear with me if my coding is sloppy or unnecessarily complicated. I am trying to create a list of songs that among other things needs to be alphabetized by title. For some of some of the songs I have listed titles in multiple languages. Is it possible to always sort alphabetically by Japanese, for example, but if there isn't one, then alphabetize by the english title. Here's an example:

<music-catalogue>
    <song>
        <title>
            <romaji>Agechikuten</romaji>
            <japanese>&#25562;&#20316;&#30000;</japanese>
        </title>
    </song>
    <song>
        <title>
            <romaji>Kamigami no Uta</romaji>
            <japanese>&#31070;&#12293;&#12398;&#35433;</japanese>
            <english>Song of the Gods</english>
        </title>
        <artist>
            <e-name>Himekami</e-name>
            <j-name>&#23019;&#31070;</j-name>
            <link>&himekami;</link>
        </artist>
    </song>
    <song>
        <title>
            <english>Freedom</english>
        </title>
        <artist>
            <e-name>12 Girls Band</e-name>
            <j-name>&#22899;&#23376;&#21313;&#20108;&#20048;&#22346;</j-name>
        </artist>
    </song>
    <song>
        <title>
            <romaji>Tinsagu nu Hana</romaji>
            <japanese>&#12486;&#12451;&#12531;&#12469;&#12464;&#12396;&#33457;</japanese>
        </title>
    </song>
</music-catalogue>

Currently, the songs are displayed by the content of the romaji element which would show:

  • Freedom
  • Agechikuten
  • Kamigami no Uta
  • Tinsagu nu Hana

Freedom is on top because there is not a romaji tag to be sorted. Is it possible to sort the songs by the content of the romaji element but if it doesn't exist, use the content of the english element to display this list:

Please let me know if I should include any more information or parts of my coding. Thank you!


I think this can be achieved by the xsl:sort element, which can be used with both xsl:for-each and xsl:apply-templates

<xsl:sort select="concat(title/romaji, title[not(romaji)]/english)" />

So, this means sort of the romaji title, but if if this no present, then the english title will be used.

Here is a simple XSLT to demonstrate

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

   <xsl:template match="/music-catalogue">
      <ol>
         <xsl:apply-templates select="song">
            <xsl:sort select="concat(title/romaji, title[not(romaji)]/english)" />
         </xsl:apply-templates>
      </ol>
   </xsl:template>

   <xsl:template match="song">
      <li>
         <xsl:value-of select="concat(title/romaji, title[not(romaji)]/english)" />
      </li>
   </xsl:template>
</xsl:stylesheet>

When applied on your sample XML, the following results are output

<ol>
   <li>Agechikuten</li>
   <li>Freedom</li>
   <li>Kamigami no Uta</li>
   <li>Tinsagu nu Hana</li>
</ol>


This is similar to the solution proposed by @Tim C, but avoids the need to use string concatenation:

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

 <xsl:template match="/*">
     <xsl:apply-templates select="song/title">
       <xsl:sort select="romaji | self::*[not(romaji)]/english"/>
     </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="title">
  <xsl:text>&#xA;</xsl:text>
  <xsl:value-of select=
   "romaji | self::*[not(romaji)]/english"/>
 </xsl:template>
</xsl:stylesheet>

When applied to the provided XML document (slightly modified to make it well-formed -- edited an undefined entity reference):

<music-catalogue>
    <song>
        <title>
            <romaji>Agechikuten</romaji>
            <japanese>&#25562;&#20316;&#30000;</japanese>
        </title>
    </song>
    <song>
        <title>
            <romaji>Kamigami no Uta</romaji>
            <japanese>&#31070;&#12293;&#12398;&#35433;</japanese>
            <english>Song of the Gods</english>
        </title>
        <artist>
            <e-name>Himekami</e-name>
            <j-name>&#23019;&#31070;</j-name>
            <link>&amp;himekami;</link>
        </artist>
    </song>
    <song>
        <title>
            <english>Freedom</english>
        </title>
        <artist>
            <e-name>12 Girls Band</e-name>
            <j-name>&#22899;&#23376;&#21313;&#20108;&#20048;&#22346;</j-name>
        </artist>
    </song>
    <song>
        <title>
            <romaji>Tinsagu nu Hana</romaji>
            <japanese>&#12486;&#12451;&#12531;&#12469;&#12464;&#12396;&#33457;</japanese>
        </title>
    </song>
</music-catalogue>

the wanted, correct result is produced:

Agechikuten
Freedom
Kamigami no Uta
Tinsagu nu Hana

Explanation:

In this solution the wanted sort key is expressed precisely using set arithmetic:

 romaji | self::*[not(romaji)]/english

where | is the XPath union operator.

Do note, that this solution works correctly even if english comes before (in document order) romaji.


The common way to achieve this kind of sort is by the union of the wanted nodes:

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

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

    <xsl:template match="music-catalogue">
        <xsl:copy>
            <xsl:apply-templates select="song">
                <xsl:sort select="title/romaji | title[not(romaji)]/english"/>
            </xsl:apply-templates>
        </xsl:copy> 
    </xsl:template>

</xsl:stylesheet>

produces:

<music-catalogue>
   <song>
      <title>
         <romaji>Agechikuten</romaji>
         <japanese>揚作田</japanese>
      </title>
   </song>
   <song>
      <title>
         <english>Freedom</english>
      </title>
      <artist>
         <e-name>12 Girls Band</e-name>
         <j-name>女子十二乐坊</j-name>
      </artist>
   </song>
   <song>
      <title>
         <romaji>Kamigami no Uta</romaji>
         <japanese>神々の詩</japanese>
         <english>Song of the Gods</english>
      </title>
      <artist>
         <e-name>Himekami</e-name>
         <j-name>姫神</j-name>
         <link/>
      </artist>
   </song>
   <song>
      <title>
         <romaji>Tinsagu nu Hana</romaji>
         <japanese>ティンサグぬ花</japanese>
      </title>
   </song>
</music-catalogue>
0

精彩评论

暂无评论...
验证码 换一张
取 消