I am trying to get my head around the functions etc in xslt with xml. I have made a dummy xml file

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="books.xslt"?>

<books>
    <book>
        <title>Hello Dolly</title>
        <author>John Thumb</author>
            <readers>
                <young_readers>17</young_readers>
                <older_readers>23</older_readers>
                <total_readers>40</total_readers>
            </readers>
    </book>
    <book>
        <title>Goodbye John Thumb</title>
        <author>Dolly Smith</author>
            <readers>
                <young_readers>12</young_readers>
                <older_readers>3</older_readers>
                <total_readers>15</total_readers>
            </readers>
    </book>
    <book>
        <title>A Boat Afloat</title>
        <author>Mike Sailor</author>
            <readers>
                <young_readers>19</young_readers>
                <older_readers>22</older_readers>
                <total_readers>41</total_readers>
            </readers>
    </book>
    <book>
        <title>Five Drummers Drumming</title>
        <author>Carol Christmas</author>
            <readers>
                <young_readers>11</young_readers>
                <older_readers>2</older_readers>
                <total_readers>13</total_readers>
            </readers>
    </book>
</books>

and what I wish to do is manipulate the reader's data to get different output. For example, sort all the young readers, find the highest and see which book this figure belongs to. The result for that would be "A Boat Afloat". I am totally new to XML and XSLT and what I know about other language functions just don't work here. This is my XSLT for the sum of readers...

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <html>
            <head>
                <meta http-equiv="content-type" content="text/html; charset=utf-8" /> 
                <title>books</title>

            </head>

<body>
    <h2>Books and Their Readers</h2>

        <p>Total Yound Readers<xsl:text> - </xsl:text><xsl:value-of select="sum((//young_readers)[.!=''])"/></p>
        <p>Total Older Readers<xsl:text> - </xsl:text><xsl:value-of select="sum((//older_readers)[.!=''])"/></p>
        <p>Grand Total Readers<xsl:text> - </xsl:text><xsl:value-of select="sum((//total_readers)[.!=''])"/></p>

</body>

        </html>
    </xsl:template>
</xsl:stylesheet>

This all works. I just want to learn the functions a bit better so any hints will help. I also feel there might be a way of adding up the readers per book without hard coding the results for total_readers. Thanks in advance

Dani AI

Generated

A compact, practical summary that builds on ’s sorting example and gives two common XSLT 1.0 patterns you can drop straight into your stylesheet: (1) pick the book that has the largest young_readers, and (2) compute a per-book total from child elements so you don’t need a hard-coded total_readers.

Select the book(s) with the maximum young_readers (XPath idiom that works in XSLT 1.0). The predicate keeps only books for which no other book has a larger young_readers value; add [1] to choose a single winner when there are ties.

<xsl:variable name="topBook"
  select="/books/book[not(/books/book/readers/young_readers > readers/young_readers)][1]"/>

<!-- then output -->
<xsl:value-of select="$topBook/title"/>
<xsl:text> by </xsl:text>
<xsl:value-of select="$topBook/author"/>

Compute a per-book total from the numeric child elements of readers so the XML element total_readers is not required. The predicate number(.) = number(.) filters out non-numeric text (number(.) produces NaN for non-numeric; NaN != NaN), avoiding NaN results from sum().

<xsl:variable name="computedTotal"
  select="sum(readers/*[number(.) = number(.)])"/>

<!-- or exclude an existing total_readers child -->
<xsl:variable name="computedTotal2"
  select="sum(readers/*[local-name() != 'total_readers' and number(.) = number(.)])"/>

Troubleshooting tips: trim whitespace with normalize-space() if numbers contain extra spaces; ensure text nodes are pure numerals (no commas/currency); [number(.) = number(.)] is a simple numeric filter in XPath 1.0. For lists, ’s xsl:apply-templates with xsl:sort is perfect; for very large documents consider keys or, if you can use XSLT 2.0/3.0, the max() function and grouping make these tasks cleaner.

Recommended Answers

All 2 Replies

with the help of xpath function and templates
one can modify the result XML

an example

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <html>
            <head>
                <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
                <title>books</title>
            </head>
            <body>
                <h2>Books and Their Readers</h2>
                <p>Total Yound Readers<xsl:text> - </xsl:text><xsl:value-of select="sum((//young_readers)[.!=''])"/></p>
                <p>Total Older Readers<xsl:text> - </xsl:text><xsl:value-of select="sum((//older_readers)[.!=''])"/></p>
                <p>Grand Total Readers<xsl:text> - </xsl:text><xsl:value-of select="sum((//total_readers)[.!=''])"/></p>
                <table border="solid">
                    <caption>bookslist</caption>
                    <tr>
                        <td>title</td>
                        <td>author</td>
                        <td>Young</td>
                        <td>old</td>
                    </tr>
                    <xsl:apply-templates select="books/book"/>
                </table>
                <p></p>
                <table border="solid">
                    <caption>Bestseller young readers</caption>
                    <tr>
                        <td>title</td>
                        <td>author</td>
                    </tr>
                    <xsl:apply-templates select="books/book">
                        <xsl:sort select="readers/young_readers" order="descending" data-type="number" />
                    </xsl:apply-templates>
                </table>
                <p></p>
                <table border="solid">
                    <caption>Bestseller older readers</caption>
                    <tr>
                        <td>title</td>
                        <td>author</td>
                    </tr>
                    <xsl:apply-templates select="books/book">
                        <xsl:sort select="readers/older_readers" order="descending" data-type="number" />
                    </xsl:apply-templates>
                </table>
                <p></p>
                <table border="solid">
                    <caption>Bestseller all readers</caption>
                    <tr>
                        <td>title</td>
                        <td>author</td>
                    </tr>
                    <xsl:apply-templates select="books/book">
                        <xsl:sort select="readers/total_readers" order="descending" data-type="number" />
                    </xsl:apply-templates>
                </table>
            </body>
        </html>
    </xsl:template>
    <xsl:template match="book">
        <tr>
            <td>
                <xsl:value-of select="title"/>
            </td>
            <td>
                <xsl:value-of select="author"/>
            </td>
            <td>
                <xsl:value-of select="readers/young_readers"/>
            </td>
            <td>
                <xsl:value-of select="readers/older_readers"/>
            </td>
            <td>
            <xsl:variable select="readers/child::*[position()&lt;3]" name="rc"/>
            <xsl:value-of select="sum($rc)"/>
            </td>
        </tr>
    </xsl:template>
</xsl:stylesheet>

result

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html">
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <title>books</title>
  </head>
  <body>
    <h2>Books and Their Readers</h2>
    <p>Total Yound Readers - 59</p>
    <p>Total Older Readers - 50</p>
    <p>Grand Total Readers - 109</p>
    <table border="solid">
      <caption>bookslist</caption>
      <tr>
        <td>title</td>
        <td>author</td>
        <td>Young</td>
        <td>old</td>
      </tr>
      <tr>
        <td>Hello Dolly</td>
        <td>John Thumb</td>
        <td>17</td>
        <td>23</td>
        <td>40</td>
      </tr>
      <tr>
        <td>Goodbye John Thumb</td>
        <td>Dolly Smith</td>
        <td>12</td>
        <td>3</td>
        <td>15</td>
      </tr>
      <tr>
        <td>A Boat Afloat</td>
        <td>Mike Sailor</td>
        <td>19</td>
        <td>22</td>
        <td>41</td>
      </tr>
      <tr>
        <td>Five Drummers Drumming</td>
        <td>Carol Christmas</td>
        <td>11</td>
        <td>2</td>
        <td>13</td>
      </tr>
    </table>
    <p></p>
    <table border="solid">
      <caption>Bestseller young readers</caption>
      <tr>
        <td>title</td>
        <td>author</td>
      </tr>
      <tr>
        <td>A Boat Afloat</td>
        <td>Mike Sailor</td>
        <td>19</td>
        <td>22</td>
        <td>41</td>
      </tr>
      <tr>
        <td>Hello Dolly</td>
        <td>John Thumb</td>
        <td>17</td>
        <td>23</td>
        <td>40</td>
      </tr>
      <tr>
        <td>Goodbye John Thumb</td>
        <td>Dolly Smith</td>
        <td>12</td>
        <td>3</td>
        <td>15</td>
      </tr>
      <tr>
        <td>Five Drummers Drumming</td>
        <td>Carol Christmas</td>
        <td>11</td>
        <td>2</td>
        <td>13</td>
      </tr>
    </table>
    <p></p>
    <table border="solid">
      <caption>Bestseller older readers</caption>
      <tr>
        <td>title</td>
        <td>author</td>
      </tr>
      <tr>
        <td>Hello Dolly</td>
        <td>John Thumb</td>
        <td>17</td>
        <td>23</td>
        <td>40</td>
      </tr>
      <tr>
        <td>A Boat Afloat</td>
        <td>Mike Sailor</td>
        <td>19</td>
        <td>22</td>
        <td>41</td>
      </tr>
      <tr>
        <td>Goodbye John Thumb</td>
        <td>Dolly Smith</td>
        <td>12</td>
        <td>3</td>
        <td>15</td>
      </tr>
      <tr>
        <td>Five Drummers Drumming</td>
        <td>Carol Christmas</td>
        <td>11</td>
        <td>2</td>
        <td>13</td>
      </tr>
    </table>
    <p></p>
    <table border="solid">
      <caption>Bestseller all readers</caption>
      <tr>
        <td>title</td>
        <td>author</td>
      </tr>
      <tr>
        <td>A Boat Afloat</td>
        <td>Mike Sailor</td>
        <td>19</td>
        <td>22</td>
        <td>41</td>
      </tr>
      <tr>
        <td>Hello Dolly</td>
        <td>John Thumb</td>
        <td>17</td>
        <td>23</td>
        <td>40</td>
      </tr>
      <tr>
        <td>Goodbye John Thumb</td>
        <td>Dolly Smith</td>
        <td>12</td>
        <td>3</td>
        <td>15</td>
      </tr>
      <tr>
        <td>Five Drummers Drumming</td>
        <td>Carol Christmas</td>
        <td>11</td>
        <td>2</td>
        <td>13</td>
      </tr>
    </table>
  </body>
</html>

in table 3 you can see that redundant data in the XML file are available

Helmut Hagemann

Helmut, thank you so much. I would never have come up with the combination of coding that is involved in getting these results. I think what you have done for me is a great start for me to see the mechanations of how the xslt functoins work. Thank you again, Patricia

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.