Determine the position of node using XSLT - xslt

Given the following XML:
<data>
<content>
<section link-id="32">
<entry id="9">
<title handle="apples">Apples</title>
</entry>
<entry id="1">
<title handle="oranges">Oranges</title>
</entry>
<entry id="4">
<title handle="pears">Pears</title>
</entry>
</section>
<section link-id="23">
<entry id="59">
<title handle="chevrolet">Chevrolet</title>
</entry>
<entry id="31">
<title handle="toyota">Toyota</title>
</entry>
<entry id="54">
<title handle="bmw">BMW</title>
</entry>
</section>
</content>
</data>
Styled by this XSL:
<xsl:template match="data">
<html>
<body>
<xsl:apply-templates select="content/section" />
</body>
</html>
</xsl:template>
<xsl:template match="content/section">
<ul>
<li>
Title: <xsl:value-of select="entry/title"/>
</li>
<li>
Position: <xsl:value-of select="position()"/>
</li>
</ul>
</xsl:template>
How would I display and integer representing the order (1-6) of the selected entry nodes? The expected values would be 1 and 4. The example shows values of 1 and 2, i.e. the position in the selected node set. What I want is the numeric position in the XML file, not the selection.

I am unclear exactly what you are asking about, as your "1 and 4" and reference to "position in the previous node set" is a bit confusing. But I think you have a couple options.
You can process all entries from the start:
<body>
<ul>
<xsl:apply-templates select="content/section/entry"/>
</ul>
</body>
...
<xsl:template match="entry">
<li>
Title: <xsl:apply-templates select="title"/>
</li>
<li>
Position: <xsl:apply-templates select="position()"/>
</li>
</xsl:template>
Or, if you find you have to process the sections and entries separately, then you'll find yourself inside of an entry with position() not helping. At this point you can use <xsl:number level="any"/>. If you are at a position deep inside of an entry, you can use <xsl:number count="entry" level="any"/>.

You are confusing "position" (an English word that describes what you want) with "position()" (an XPath function that gives you something quite different).
Try
<xsl:for-each select="entry[1]">
<xsl:number level="any" from="content"/>
</xsl:for-each>
It looks as if you are deliberately using the XSLT 1.0 "feature" whereby xsl:value-of applied to a node-set ignores all nodes except the first. If you want your code to be compatible with 2.0 (and understandable to the reader) it's best to make this explicit by writing select="entry[1]/title".

Related

XSLT - For-each-group

This is a really basic question, but I'm not understanding how the for-each-group works. I want to combine adjacent top-level sections that don't have a sub section into a list. If there are sections WITH subsections I want to treat those differently, leaving the top level intact and combining the sub-sections into a list. I don't want to mix these up.
Source XML
<?xml version="1.0" encoding="UTF-8"?>
<body>
<sec>
<title>A1</title>
<p>Stuff A 1</p>
</sec>
<sec>
<title>A2</title>
<p>Stuff A 2</p>
</sec>
<sec>
<title>A3</title>
<p>Stuff A 3</p>
<sec>
<title>B1</title>
<p>Stuff B1</p>
</sec>
<sec>
<title>B2</title>
<p>Stuff B2</p>
</sec>
</sec>
<sec>
<title>A4</title>
<p>Stuff A 4</p>
</sec>
</body>
Desired Result
<body>
<list>
<list-item><title>A1</title><p>Stuff A 1</p></list-item>
<list-item><title>A2</title><p>Stuff A 2</p></list-item>
</list>
<sec>
<title>A3</title>
<p>Stuff A 3</p>
<list>
<list-item><title>B1</title><p>Stuff B1</p></list-item>
<list-item><title>B2</title><p>Stuff B2</p></list-item>
</list>
</sec>
<list>
<list-item><title>A4</title><p>Stuff A 4</p></list-item>
</list>
</body>
XSLT fragment
This is definitely not correct. Also, it is not the only way I've tried, just the least messy to post. The way I think for-each-group should work I keep getting the error An empty sequence is not allowed as the #group-adjacent attribute of xsl:for-each-group. So this is just a snippet to get someone who knows what they are doing started.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<!-- Identity Template -->
<xsl:template match="#*|node()" name="default" mode="#all">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="#current"/>
</xsl:copy>
</xsl:template>
<!-- Make top level body tag -->
<xsl:template match="body">
<body>
<xsl:for-each-group select="sec[not(sec)]" group-adjacent=".">
<list>
<xsl:apply-templates select="current-group()"/>
</list>
</xsl:for-each-group>
</body>
</xsl:template>
<xsl:template match="sec[not(sec)]">
<list-item>
<xsl:copy-of select="*"/>
</list-item>
</xsl:template>
</xsl:stylesheet>
Try
<xsl:for-each-group select="sec" group-adjacent="exists(child::sec)">
which will give a group of sec elements that have a sec child, followed by a group that don't, and so on.
Within the for-each-group you may need to do <xsl:choose><xsl:when test="child::sec">... to apply different processing to the two kinds of group.

Apply templates not working as desired

Given the following xml inputs:
file1:
<?xml version="1.0" encoding="UTF-8"?>
<File1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<code code="file1_code" displayName="file1_display" codeSystem="file1_cs" codeSystemName="file1_csn"/>
<title>Title of file1</title>
<component typeCode="COMP">
<structuredBody classCode="DOCBODY">
<component typeCode="COMP">
<section>
<templateId root="someRoot_file1" assigningAuthorityName="someAuhthority_file1"/>
<code code="file1-sec1_code" displayName="file1_sec1_display" codeSystem="file1_sec1_cs" codeSystemName="file1_sec1_csn"/>
<title>Tile of sec 1 from file1</title>
<text>
<content styleCode="Italics">
Text of sec 1 from file1
</content>
</text>
<entry> file 1 sec 1
</entry>
</section>
</component>
<component typeCode="COMP">
<section classCode="DOCSECT">
<code code="file1_sec2_code" codeSystem="file2_sec2_cs" displayName="file2_sec2_display" codeSystemName="file2_sec2_csn"/>
<title>Tile from sec 2 file 1</title>
<text>
<content styleCode="Italics">
Text from file1 sec 2
</content>
</text>
<entry typeCode="test"> file2 sec 2
</entry>
</section>
</component>
</structuredBody>
</component>
</File1>
file2:
<?xml version="1.0"?>
<A>
<title value="Title of file2"/>
<text>
<status value="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">
<p>File 2 Text</p>
</div>
</text>
<section>
<code>
<coding>
<system value="sec 1 file2 sys"/>
<code value="sec 1 file 2 code"/>
<display value="sec 1 file 2 display"/>
</coding>
</code>
<title>Title of sec 1 file2</title>
<text>
<content styleCode="Italics">Section 1 Text
</content>
</text>
<entry>
<someEntry>
</someEntry>
</entry>
</section>
<section>
<code>
<coding>
<system value="sec 2 file2 sys"/>
<code value="sec 2 file 2 code"/>
<display value="sec 2 file 2 display"/>
</coding>
</code>
<title>Title of sec 2 file2</title>
<text>
<content styleCode="Italics">Section 2 file2 Text
</content>
</text>
<entry>
<someEntry> entry sec 2 file 2
</someEntry>
</entry>
</section>
</A>
and the following xslt:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*" />
<xsl:variable name="input" select="/" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<Bundle>
<id value="test"/>
<type value="document"/>
<entry>
<resource>
<xsl:apply-templates select="document('file2.xml')/*"/>
</resource>
</entry>
</Bundle>
</xsl:template>
<xsl:template match="text">
<text>
<status value="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">
<p>This is the text from the stylesheet </p>
</div>
</text>
</xsl:template>
<xsl:template match="title">
<xsl:apply-templates select="$input/File1/title"/>
</xsl:template>
<xsl:template match="section[1]">
<xsl:apply-templates select="$input/File1/component/structuredBody/component/section"/>
</xsl:template>
<xsl:template match="section[2]"/>
<xsl:template match="File1/title">
<title>
<xsl:attribute name="value">
<xsl:value-of select="." />
</xsl:attribute>
</title>
</xsl:template>
<xsl:template match = "File1/component/structuredBody/component/section">
<section>
<xsl:apply-templates/>
</section>
</xsl:template>
</xsl:stylesheet>
And this is the output:
<?xml version="1.0" encoding="UTF-8"?>
<Bundle>
<id value="test"/>
<type value="document"/>
<entry>
<resource>
<A>
<title value="Title of file1"/>
<text>
<status value="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">
<p>This is the text from the stylesheet </p>
</div>
</text>
<section>
<templateId xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" root="someRoot_file1" assigningAuthorityName="someAuhthority_file1"/>
<code xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" code="file1-sec1_code" displayName="file1_sec1_display" codeSystem="file1_sec1_cs" codeSystemName="file1_sec1_csn"/>
<title value="Title of file1"/>
<text>
<status value="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">
<p>This is the text from the stylesheet </p>
</div>
</text>
<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
</section>
<section>
<code xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" code="file1_sec2_code" codeSystem="file2_sec2_cs" displayName="file2_sec2_display" codeSystemName="file2_sec2_csn"/>
<title value="Title of file1"/>
<text>
<status value="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">
<p>This is the text from the stylesheet </p>
</div>
</text>
<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" typeCode="test"/>
</section>
</A>
</resource>
</entry>
</Bundle>
And this is the expected output:
<?xml version="1.0" encoding="UTF-8"?>
<Bundle>
<id value="test"/>
<type value="document"/>
<entry>
<resource>
<A>
<title value="Title of file1"/>
<text>
<status value="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">
<p>This is the text from the stylesheet </p>
</div>
</text>
<section>
<templateId xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" root="someRoot_file1" assigningAuthorityName="someAuhthority_file1"/>
<code xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" code="file1-sec1_code" displayName="file1_sec1_display" codeSystem="file1_sec1_cs" codeSystemName="file1_sec1_csn"/>
<title>Tile of sec 1 from file1</title>
<text>
<content styleCode="Italics">
Text of sec 1 from file1
</content>
</text>
<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
</section>
<section>
<code xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" code="file1_sec2_code" codeSystem="file2_sec2_cs" displayName="file2_sec2_display" codeSystemName="file2_sec2_csn"/>
<title>Tile from sec 2 file 1</title>
<text>
<content styleCode="Italics">
Text from file1 sec 2
</content>
</text>
<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" typeCode="test"/>
</section>
</A>
</resource>
</entry>
</Bundle>
I have the following questions:
Why is the title in the section elements coming from the main title (i.e. File1/title) when the apply templates is within File1/component/structuredBody/component/section? I was expecting that the title of the section will be output, which is what is desired. Even more confusing is that it does indeed output the elements in the section like code, entry and so on but title and text (see q2 below) seems to be treated differently and I can't for the life of me understand why.
Same with text. Why is the text for section not being output?
Here is my presumably false understanding of the process:
We start with the <xsl:template match="/"> and create elements Bundle, id etc. and then using <xsl:apply-templates select="document('file2.xml')/*"/> we match the top element of file2 (A) and since we don't have a template matching it explicitly, the identity template is called, copies it and process its child elements, which are text, title and section. For each of these child elements, it looks for a matching template. it finds them and matches them.
For element section however, it matches only the first section element because of <xsl:template match="section[1]"> and then because of <xsl:apply-templates select="$input/File1/component/structuredBody/component/section"/> in the template, it looks for a template matching children of section in FIle1, which are code, text, title and templateId. It finds no such explicitly defined template, so calls the identity templates for them, copies and processes them till the end. At least that is my understanding of it.
Why is the title in the section elements coming from the main title
Because any time the processor is instructed to apply templates to a title, it looks for the best-matching template to apply, and finds this:
<xsl:template match="title">
<xsl:apply-templates select="$input/File1/title"/>
</xsl:template>
This changes the context to the title in File1.xml, and the best-matching template for this one is:
<xsl:template match="File1/title">
<title>
<xsl:attribute name="value">
<xsl:value-of select="." />
</xsl:attribute>
</title>
</xsl:template>
and that is the result you see.
Same with text. Why is the text for section not being output?
-- edited in response to the following clarification: --
When I say text I am talking about text elements only.
The original text element (child of section in File1.xml) is not being output because you have a specific template matching it and outputting something else instead:
<xsl:template match="text">
<text>
<status value="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">
<p>This is the text from the stylesheet </p>
</div>
</text>
</xsl:template>
# michael.hor257k and #Michael Kay yeah, that was definitely the case, that I misunderstood how xsl:apply-templates works with regards to context . I thought because I called the xsl:apply-templates from within xsl:template match = "$input/File1/component/structuredBody/component/section"> that it will only look for match templates that match the children of section. In other words, I thought it will look for templates like “<xsl:template match=”File1/component/structuredBody/component/section/title"> but that is clearly not the case.
xsl:apply-templates simply looks for the children and then looks for a match template regardless of the context from which they were called. So, it will look for title or text template that matches and if it finds them, it will match them.
The easiest solution I could find that seems to solve the problem is to add a path to the title and text templates. In other words, instead of just <xsl:template match="text"> I should have <xsl:template match="A/text">. Same for title. This way, the xsl:apply-templates will not apply <xsl:template match="A/text"> as the title in section is not a child of A. So given that no matching explicit template is defined, the identity template will be applied and will output the title of section as desired.

Compare a processing instruction and get the file name

I'm writing an XSLT in which I need to see if a value is in preprocessing instruction. In my XML, the preprocessing looks like below.
<?xpp MAIN;1;1;0;0;69;0;0?>
Sample XML
<root>
<?xpp MAIN;1;0;0;0;73;0;0?>
<preface role="guideline">
<title>title</title>
<section role="group">
<para>data</para>
</section>
<section role="group">
<title>
<?xpp lp;0.5p?>Licences</title>
<itemizedlist mark="bullet">
<listitem>
<para>itemdata</para>
</listitem>
</itemizedlist>
</section>
<section role="group">
<title>
<?xpp lp;0.5p?>Letters to the Editor</title>
<itemizedlist mark="bullet">
<listitem>
<para><?xpp MAIN;1;0;0;0;74;0;0?>item data</para>
</listitem>
</itemizedlist>
</section>
</preface>
</root>
Sample1 XML
<root>
<?xpp MAIN;1;0;0;0;83;0;0?>
<preface role="guideline">
<title>title</title>
<section role="group">
<para>data</para>
</section>
<section role="group">
<title>
<?xpp lp;0.5p?>Licences</title>
<itemizedlist mark="bullet">
<listitem>
<para>itemdata</para>
</listitem>
</itemizedlist>
</section>
<section role="group">
<title>
<?xpp lp;0.5p?>Letters to the Editor</title>
<itemizedlist mark="bullet">
<listitem>
<para><?xpp MAIN;1;0;0;0;84;0;0?>item data</para>
</listitem>
</itemizedlist>
</section>
</preface>
</root>
For the below XML, I need to do the task of going through files and checking for the data. The Above sample XML and Sample1 XML are the files that I've loop through.
<root>
<table frame="none" tabstyle="wrap4">
<tgroup cols="4">
<colspec colnum="1" colname="col1" colwidth="10*"/>
<colspec colnum="2" colname="COLSPEC0" colwidth="10.00*"/>
<colspec colnum="3" colname="col2" colwidth="275*"/>
<colspec colnum="4" colname="col3" colwidth="15*"/>
<tbody>
<row>
<entry colsep="0" rowsep="0">Text1</entry>
<entry colsep="0" rowsep="0"/>
<entry colsep="0" rowsep="0"/>
<entry colsep="0" rowsep="0" align="right">75</entry>
</row>
<row>
<entry colsep="0" rowsep="0">Text2</entry>
<entry colsep="0" rowsep="0"/>
<entry colsep="0" rowsep="0"/>
<entry colsep="0" rowsep="0" align="right">84</entry>
</row>
</tbody>
</tgroup>
</table>
</root>
Here first I'm trying to loop through all the files available, in each file, look for the preprocessing containing MAIN in it and then see if my value is present in that preprocessing instruction. Below is my XSLT block.
<xsl:template match="entry[#align='right']" mode="y">
<xsl:analyze-string select="." regex="([0-9]+)">
<xsl:matching-substring>
<xsl:variable name="prent" select="document(document('file:///C:\Users\userId\Desktop\Proview\AUS Journal\02FEB/title.xml')/entry/file/#name)/*[contains(//root/processing-instruction('xpp')[contains(.,'MAIN')],regex-group(1))]/substring-before(tokenize(document-uri(/), '/')[last()], '.')"/>
<xsl:variable name="cha">
<xsl:value-of select="$prent"/>
</xsl:variable>
<xsl:variable name="size">
<xsl:value-of select="string-length($cha)"/>
</xsl:variable>
<xsl:variable name="conct">
<xsl:value-of select="concat($cha,'/pg_',.)"/>
</xsl:variable>
<a href="{$conct}">
<xsl:value-of select="regex-group(1)"/>
</a>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
When I run this I get the below error.
XSLT 2.0 Debugging Error: Error: file:///C:/Users/userId/Desktop/Proview/AUS%20Journal/02FEB/XSLT/Journal.xsl:674: Wrong occurrence to match required sequence type - Details: - XPTY0004: The supplied sequence ('2' item(s)) has the wrong occurrence to match the sequence type xs:string ('zero or one').
please let me know how can I fix this and get job done.
Thanks
Try whether changing the step
*[contains(//root/processing-instruction('xpp')[contains(.,'MAIN')],regex-group(1))]
to
*[//root/processing-instruction('xpp')[contains(.,'MAIN')][contains(., regex-group(1))]]
fixes the problem.

Build a tree from a XML file using XSLT?

Being a XML file, I know to generate some output by the XSL. But I need to build a navigation tree either to hidden the contents that I don't have to look at or show those I want to. Tree that I am looking for should look like this one:
+VIEW1
+VIEW2
if hitting something like '+' somewhere, for instance on VIEW2 , we should get the content of VIEW2 like this one:
+VIEW1
-VIEW2
yy NO
aa YES
zz NO
tt NO
Here's a part of my XML file and "view.xsl" which i wrote. I also tried to modify some examples from the stackoverflow but i didn't find how to get right solution.
view.xsl
<xsl:stylesheet version="2.0"
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" cdata-section-elements="Cdata" indent="yes"/>
<xsl:template match = "/">
<html >
<head>
<title Localizable_1="True"><xsl:value-of select="DOC/show"/></title>
</head>
<BODY class="BODY">
<H1><xsl:value-of select="DOC/show"/></H1>
<TABLE WIDTH="500px">
<xsl:for-each select="DOC/Entry">
<xsl:call-template name="RULE"/>
</xsl:for-each>
</TABLE>
</BODY>
</html>
</xsl:template>
<xsl:template name="RULE">
<xsl:choose>
<xsl:if test="level='ON'"><xsl:value-of select="light"/>YES</xsl:if>
<xsl:if test="level='OFF'"><xsl:value-of select="light"/>NO</xsl:if>
</xsl:choose>
file.XML
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml:stylesheet type='text/xsl' href='view.xsl'?>
<DOC>
<show>VIEW1</show>
<Entry>
<light>ae</light>
<level>ON</level>
</Entry>
<Entry>
<light>by</light>
<level>OFF</level>
</Entry>
<Entry>
<light>ac</light>
<level>OFF</level>
</Entry>
<show>VIEW2</show>
<Entry>
<light>yy</light>
<level>OFF</level>
</Entry>
<Entry>
<light>aa</light>
<level>ON</level>
</Entry>
<Entry>
<light>zz</light>
<level>OFF</level>
</Entry>
<Entry>
<light>tt</light>
<level>OFF</level>
</Entry>
</DOC>
Thanks for any help
This works (I build a table with tr and td elements but I assume you can enhance from here).
Helpful links on following-sibling
<xsl:stylesheet version="2.0"
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/1999/xhtml" >
<xsl:output method="html" cdata-section-elements="Cdata" indent="yes"/>
<xsl:template match = "/">
<html >
<head>
<title Localizable_1="True">
<xsl:value-of select="DOC/show"/>
</title>
<script src="http://code.jquery.com/jquery-1.9.1.min.js" type="text/javascript">
<xsl:comment>jq</xsl:comment>
</script>
<script src="https://raw.github.com/vakata/jstree/master/dist/jstree.min.js" type="text/javascript">
<xsl:comment>tree</xsl:comment>
</script>
</head>
<body class="BODY">
<div class="tree">
<xsl:apply-templates select="DOC/show" />
</div>
<script>
$('div.tree')
.jstree({
core: {}
})
.bind("select_node.jstree", function (event, data) {
alert(data.node.context.id); /* do clever things here */
})
.delegate("a", "click", function (event, data) { event.preventDefault(); });
</script>
</body>
</html>
</xsl:template>
<!-- match show elements -->
<xsl:template match="show">
<ul class="tree">
<li>
<a>
<xsl:value-of select="."/>
</a>
<ul>
<!-- only select the next Entry element -->
<xsl:apply-templates select="following-sibling::*[1][self::Entry] "/>
</ul>
</li>
</ul>
</xsl:template>
<xsl:template match="Entry">
<li class="rule">
<xsl:call-template name="RULE"/>
</li>
<!-- only select the next Entry element -->
<xsl:apply-templates select="following-sibling::*[1][self::Entry] "/>
</xsl:template>
<xsl:template name="RULE">
<xsl:element name="a">
<!-- or have a href here -->
<xsl:attribute name="id">
<xsl:value-of select="light"/>
</xsl:attribute>
<span class="light">
<xsl:value-of select="light"/>
</span>
<span class="level">
<xsl:choose>
<xsl:when test="level='ON'">
<xsl:text>YES</xsl:text>
</xsl:when>
<xsl:when test="level='OFF'">
<xsl:text>NO</xsl:text>
</xsl:when>
</xsl:choose>
</span>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

XSLT 1.0 Grouping on multiple values on multiple levels

edited in response to comments *
Hello,
I am an XSLT noob and need some help. I am trying to do an filter/group combination with XSLT 1.0 (can't use XSLT 2.0 for this application).
Here is an example of the xml
<entry>
<item>
<name>Widget 2</name>
<rank>2</rank>
<types>
<type>Wood</type>
<type>Fixed</type>
<type>Old</type>
</types>
</item>
<item>
<name>Widget 1</name>
<rank>2</rank>
<types>
<type>Metal</type>
<type>Broken</type>
<type>Old</type>
</types>
</item>
<item>
<name>Widget 3</name>
<rank>1</rank>
<types>
<type>Metal</type>
<type>New</type>
</types>
</item>
</entry>
Now what I want to do is output html where I get a subset of the XML based on <type> and then group on rank. For example, if the user selects all items with the type Metal, the output should be:
<p class="nospace"><font color="#800000">
<b>Rank 1</b></font></p>
<li id="mylist"><b>Widget 3</b></li>
<br\>
<p class="nospace"><font color="#800000">
<b>Rank 2</b></font></p>
<li id="mylist"><b>Widget 1</b></li>
<br\>
of if the user user chooses the type Old the output would be
<p class="nospace"><font color="#800000">
<b>Rank 2</b></font></p>
<li id="mylist"><b>Widget 1</b></li>
<li id="mylist"><b>Widget 2</b></li>
<br\>
I can group using keys on rank along easily enough, but trying to do both is not working. Here is a sample of the xslt I have tried:
<xsl:param name="typeParam"/>
<xsl:key name="byRank" use="rank" match="item"/>
<xsl:for-each select="item[count(.|key('byRank',rank)[1])=1]">
<xsl:sort data-type="number" select="rank"/>
<xsl:for-each select="key('byRank',rank)">
<xsl:sort select="name"/>
<xsl:if test="count(rank)>0">
<p class="nospace"><font color="#800000"><b>Rank<xsl:value-of select="rank"/></b></font></p>
<xsl:for-each select="types[types=$typeParam]">
<li id="mylist"><b><xsl:value-of select="../name"/></b></li>
</xsl:for-each>
<br/>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
The result I get from this is I do indeed get the subset of my xml that I want but it also displays all of the various rank values. I want to limit it to just the ranks of the type that is specified in $typeParam.
I have tried moving the for-each statement to earlier in the code as well as modifying the if statement to select for $typeParam but neither works. I have also tried concat-ing my key with rank and type but that doesn't seem to work either (It only works if the type in $typeParam is the first child under types).
Thanks
jeff
This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="kItemByRank" match="item" use="rank"/>
<xsl:param name="pType" select="'Old'"/>
<xsl:template match="entry">
<xsl:for-each select="item[count(.|key('kItemByRank',rank)[1])=1]">
<xsl:sort select="rank" data-type="number"/>
<xsl:variable name="vGroup" select="key('kItemByRank',rank)[
types/type = $pType
]"/>
<xsl:if test="$vGroup">
<p class="nospace">
<font color="#800000">
<b>
<xsl:value-of select="concat('Rank ',rank)"/>
</b>
</font>
</p>
<xsl:apply-templates select="$vGroup">
<xsl:sort select="name"/>
</xsl:apply-templates>
<br/>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template match="item">
<li id="mylist">
<b>
<xsl:value-of select="name"/>
</b>
</li>
</xsl:template>
</xsl:stylesheet>
Output:
<p class="nospace">
<font color="#800000">
<b>Rank 1</b>
</font>
</p>
<li id="mylist">
<b>Widget 3</b>
</li>
<br />
<p class="nospace">
<font color="#800000">
<b>Rank 2</b>
</font>
</p>
<li id="mylist">
<b>Widget 1</b>
</li>
<br />
And whit pType param set to 'Old', output:
<p class="nospace">
<font color="#800000">
<b>Rank 2</b>
</font>
</p>
<li id="mylist">
<b>Widget 1</b>
</li>
<li id="mylist">
<b>Widget 2</b>
</li>
<br />