I have flat xml structure, i need to convert into hierarchy. With the help of stackoverflow I was able to do it.
Question: Is it possible to show only one branch using the same flat structure?
Here is my xml and xsl files:
XML
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="Stack.xsl"?>
<Items>
<Item>
<Id>1</Id>
<ParentId>0</ParentId>
<Name>1</Name>
<SortOrder>0</SortOrder>
</Item>
<Item>
<Id>2</Id>
<ParentId>1</ParentId>
<Name>1.1</Name>
<SortOrder>0</SortOrder>
</Item>
<Item>
<Id>3</Id>
<ParentId>1</ParentId>
<Name>1.2</Name>
<SortOrder>0</SortOrder>
</Item>
<Item>
<Id>4</Id>
<ParentId>1</ParentId>
<Name>1.3</Name>
<SortOrder>0</SortOrder>
</Item>
<Item>
<Id>5</Id>
<ParentId>1</ParentId>
<Name>1.4</Name>
<SortOrder>0</SortOrder>
</Item>
<Item>
<Id>6</Id>
<ParentId>0</ParentId>
<Name>2</Name>
<SortOrder>0</SortOrder>
</Item>
<Item>
<Id>7</Id>
<ParentId>6</ParentId>
<Name>2.1</Name>
<SortOrder>0</SortOrder>
</Item>
<Item>
<Id>8</Id>
<ParentId>6</ParentId>
<Name>2.2</Name>
<SortOrder>0</SortOrder>
</Item>
<Item>
<Id>9</Id>
<ParentId>0</ParentId>
<Name>3</Name>
<SortOrder>0</SortOrder>
</Item>
<Item>
<Id>10</Id>
<ParentId>3</ParentId>
<Name>1.2.1</Name>
<SortOrder>0</SortOrder>
</Item>
<Item>
<Id>11</Id>
<ParentId>8</ParentId>
<Name>2.2.1</Name>
<SortOrder>0</SortOrder>
</Item>
<Item>
<Id>11</Id>
<ParentId>5</ParentId>
<Name>1.4.1</Name>
<SortOrder>0</SortOrder>
</Item>
</Items>
XSL
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/1999/xhtml">
<xsl:param name="SelectedId" select="'10'"/>
<xsl:key name="ChildNodes" match="/Items/Item" use="ParentId"/>
<xsl:template match="Items">
<ul>
<xsl:apply-templates select="Item[ParentId = 0]" />
</ul>
</xsl:template>
<xsl:template match="Item">
<li>
<xsl:choose>
<xsl:when test="Id = $SelectedId">
<b><xsl:value-of select="Name" /></b>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="Name" />
</xsl:otherwise>
</xsl:choose>
<xsl:variable name="Descendants" select="key ('ChildNodes', Id)" />
<xsl:if test="count ($Descendants) > 0">
<ul>
<xsl:apply-templates select="$Descendants" />
</ul>
</xsl:if>
</li>
</xsl:template>
</xsl:stylesheet>
Current output I have:
1
1.1
1.2
1.2.1
1.3
1.4
1.4.1
2
2.1
2.2
2.2.1
3
Desireable result example:
1
1.1
1.2
1.2.1
1.3
1.4
2
3
One way to do this is to make use of node-set function, which will require the use of an extension namespace in XSLT.
What you could do is that instead of outputing the Descendants variable directly as currently:
<ul>
<xsl:apply-templates select="$Descendants"/>
</ul>
You instead store the results in a variable
<xsl:variable name="list">
<ul>
<xsl:apply-templates select="$Descendants"/>
</ul>
</xsl:variable>
Then you can convert this 'result tree fragment' into a node-set, which you can then check for whether the selected element (held in a b element) exists. If so, you can then output it
<xsl:if test="exsl:node-set($list)//li[b]">
<xsl:copy-of select="$list"/>
</xsl:if>
Here is the full XSLT
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="exsl">
<xsl:output method="html"/>
<xsl:param name="SelectedId" select="'10'"/>
<xsl:key name="ChildNodes" match="/Items/Item" use="ParentId"/>
<xsl:template match="Items">
<ul>
<xsl:apply-templates select="Item[ParentId = 0]"/>
</ul>
</xsl:template>
<xsl:template match="Item">
<li>
<xsl:choose>
<xsl:when test="Id = $SelectedId">
<b>
<xsl:value-of select="Name"/>
</b>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="Name"/>
</xsl:otherwise>
</xsl:choose>
<xsl:variable name="Descendants" select="key ('ChildNodes', Id)"/>
<xsl:if test="count ($Descendants) > 0">
<xsl:variable name="list">
<ul>
<xsl:apply-templates select="$Descendants"/>
</ul>
</xsl:variable>
<xsl:if test="exsl:node-set($list)//li[b]">
<xsl:copy-of select="$list"/>
</xsl:if>
</xsl:if>
</li>
</xsl:template>
</xsl:stylesheet>
When applied to your sample XML, the following is output
<ul>
<li>1
<ul>
<li>1.1</li>
<li>1.2
<ul>
<li>
<b>1.2.1</b>
</li>
</ul></li>
<li>1.3</li>
<li>1.4</li>
</ul></li>
<li>2</li>
<li>3</li>
</ul>
Note, because I am using Microsoft XML here, the extension namespace is "urn:schemas-microsoft-com:xslt". For other processors, you will probably have to use "http://exslt.org/common"
Related
I am trying to remove duplicates from my xml based on a condition in XSLT1.0
Here is the input xml.
<?xml version="1.0" encoding="UTF-8"?>
<Envelope
xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Header>
<MessageId>{D5B72T7A-58E0-4930-9CEB-A06RT56AR21B0}</MessageId>
<Action>http://tempuri.org/TRH_FinalQueryService/find</Action>
</Header>
<Body>
<MessageParts
xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<TRH_FinalQuery
xmlns="http://schemas.microsoft.com/dynamics/2008/01/documents/TRH_FinalQuery">
<TRH_UnionView class="entity">
<Company>1</Company>
<CS/>
<Text_1>1</Text_1>
<Text_2>Lotion</Text_2>
<WS/>
</TRH_UnionView>
<TRH_UnionView class="entity">
<Company>1</Company>
<CS>1</CS>
<Text_1>1</Text_1>
<Text_2>Soap</Text_2>
<WS>6</WS>
</TRH_UnionView>
<TRH_UnionView class="entity">
<Company>2</Company>
<CS/>
<Text_1>5</Text_1>
<Text_2>Shampoo</Text_2>
<WS/>
</TRH_UnionView>
<TRH_UnionView class="entity">
<Company>2</Company>
<CS/>
<Text_1>5</Text_1>
<Text_2>Shampoo</Text_2>
<WS/>
</TRH_UnionView>
</TRH_FinalQuery>
</MessageParts>
</Body>
</Envelope>
Here is the xslt that I have applied.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:m="http://schemas.microsoft.com/dynamics/2011/01/documents/Message" xmlns:r="http://schemas.microsoft.com/dynamics/2008/01/documents/TRH_FinalQuery" exclude-result-prefixes="m r">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:key name="r:TRH_FinalQuery" match="r:TRH_FinalQuery" use="concat(r:Text_1, '|', r:Company)" />
<!-- move all elements to no namespace -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="r:TRH_FinalQuery[r:TRH_UnionView[#class='entity']/r:WessexCostCenter=''][key('r:TRH_FinalQuery',concat(r:Text_1, '|', r:Company))[1]]"/>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*" />
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<!-- removes Envelope -->
<xsl:template match="m:Envelope">
<xsl:apply-templates />
</xsl:template>
<!-- removes Header,MessageId,Action and Body -->
<xsl:template match="m:*">
<xsl:apply-templates select="*" />
</xsl:template>
<!-- rename MessageParts to Document + skip the Run wrapper -->
<xsl:template match="m:MessageParts">
<DocumentElement>
<xsl:apply-templates select="r:TRH_FinalQuery/*" />
</DocumentElement>
</xsl:template>
<!-- rename RunObject to Item -->
<xsl:template match="r:TRH_UnionView[#class='entity']">
<xsl:choose>
<xsl:when test="r:WS!=''">
<Item>
<Text_1>
<xsl:value-of select="r:WS" />
</Text_1>
<Text_2>WS BodayWash</Text_2>
<Company>
<xsl:value-of select="r:Text_1" />
</Company>
</Item>
<Item>
<Text_1>
<xsl:value-of select="r:WS" />
</Text_1>
<Text_2>WS BodayWash</Text_2>
<Company>0123</Company>
</Item>
</xsl:when>
<xsl:otherwise>
<Item>
<xsl:apply-templates select="r:Text_1" />
<xsl:apply-templates select="r:Text_2" />
<xsl:apply-templates select="r:Company" />
</Item>
<Item>
<xsl:apply-templates select="r:Text_1" />
<xsl:apply-templates select="r:Text_2" />
<Company>0123</Company>
</Item>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Below is the output I am getting
<?xml version="1.0" encoding="utf-8"?>
<DocumentElement>
<Item>
<Text_1>1</Text_1>
<Text_2>Lotion</Text_2>
<Company>1</Company>
</Item>
<Item>
<Text_1>1</Text_1>
<Text_2>Lotion</Text_2>
<Company>0123</Company>
</Item>
<Item>
<Text_1>6</Text_1>
<Text_2>WS BodayWash</Text_2>
<Company>1</Company>
</Item>
<Item>
<Text_1>6</Text_1>
<Text_2>WS BodayWash</Text_2>
<Company>0123</Company>
</Item>
<Item>
<Text_1>5</Text_1>
<Text_2>Shampoo</Text_2>
<Company>2</Company>
</Item>
<Item>
<Text_1>5</Text_1>
<Text_2>Shampoo</Text_2>
<Company>0123</Company>
</Item>
</DocumentElement>
Below is the expected output
<?xml version="1.0" encoding="utf-8"?>
<DocumentElement>
<Item>
<Text_1>6</Text_1>
<Text_2>WS BodayWash</Text_2>
<Company>1</Company>
</Item>
<Item>
<Text_1>6</Text_1>
<Text_2>WS BodayWash</Text_2>
<Company>0123</Company>
</Item>
<Item>
<Text_1>5</Text_1>
<Text_2>Shampoo</Text_2>
<Company>2</Company>
</Item>
<Item>
<Text_1>5</Text_1>
<Text_2>Shampoo</Text_2>
<Company>0123</Company>
</Item>
</DocumentElement>
I am trying to remove all duplicates based on condition
If the Text_1 and Company are same.
If the point 1 is true then retain all records having value in WS tag and remove records where there no value in WS tag.
Can you please suggest what I am doing wrong
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:m="http://schemas.microsoft.com/dynamics/2011/01/documents/Message"
xmlns:r="http://schemas.microsoft.com/dynamics/2008/01/documents/TRH_FinalQuery"
exclude-result-prefixes="m r">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:key name="myKey" match="r:TRH_UnionView" use="concat(r:Text_1, '|', r:Company)" />
<!-- Simplify things by not having an identity. Using this approach, you will not have to suppress
any elements.
-->
<xsl:template match="node()">
<xsl:apply-templates select="node()"/>
</xsl:template>
<!-- Start at the root. -->
<xsl:template match="/">
<DocumentElement>
<xsl:apply-templates select="node()" />
</DocumentElement>
</xsl:template>
<xsl:template match="r:TRH_UnionView">
<xsl:choose>
<!-- Handle the duplicates with no value in the WS tag. -->
<xsl:when test="count(key('myKey',concat(r:Text_1, '|', r:Company))) > 1 and
count((key('myKey',concat(r:Text_1, '|', r:Company)))[r:WS!='']) = 0">
<!-- Is this the first of the duplicates? -->
<xsl:if test="generate-id(.) = generate-id(key('myKey',concat(r:Text_1, '|', r:Company))[1])">
<Item>
<Text_1>
<xsl:value-of select="r:Text_1"/>
</Text_1>
<Text_2>
<xsl:value-of select="r:Text_2"/>
</Text_2>
<Company>
<xsl:value-of select="r:Company"/>
</Company>
</Item>
<Item>
<Text_1>
<xsl:value-of select="r:Text_1"/>
</Text_1>
<Text_2>
<xsl:value-of select="r:Text_2"/>
</Text_2>
<Company>0123</Company>
</Item>
</xsl:if>
</xsl:when>
<!-- Handle the duplicates with value at least one value in the WS tag. -->
<xsl:when test="count(key('myKey',concat(r:Text_1, '|', r:Company))) > 1">
<xsl:if test="r:WS!=''">
<Item>
<Text_1>
<xsl:value-of select="r:WS" />
</Text_1>
<Text_2>WS BodayWash</Text_2>
<Company>
<xsl:value-of select="r:Text_1" />
</Company>
</Item>
<Item>
<Text_1>
<xsl:value-of select="r:WS" />
</Text_1>
<Text_2>WS BodayWash</Text_2>
<Company>0123</Company>
</Item>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<Item>
<Text_1>
<xsl:value-of select="r:Text_1"/>
</Text_1>
<Text_2>
<xsl:value-of select="r:Text_2"/>
</Text_2>
<Company>
<xsl:value-of select="r:Company"/>
</Company>
</Item>
<Item>
<Text_1>
<xsl:value-of select="r:Text_1"/>
</Text_1>
<Text_2>
<xsl:value-of select="r:Text_2"/>
</Text_2>
<Company>0123</Company>
</Item>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
will appreciate your help. i have xml as following:
<ul>
<li>list item 1 </li>
<li>List Item 2
<ul>
<li>List item 2.1</li>
<li>List Item 2.2</li>
</ul>
</li>
<li>List Item 3 </li>
</ul>
The output should be as following:
<list>
<item>
<paragraph>list item 1 </paragraph>
</item>
<item>
<paragraph>List Item 2 </paragraph>
<list>
<item>
<paragraph>List<emphasis> item</emphasis> 2.1 </paragraph>
</item>
<item>
<paragraph>List Item 2.2 </paragraph>
</item>
</list>
</item>
<item>
<paragraph>List Item 3 </paragraph>
</item>
</list>
I am using xlst version 3.0 as following:
<xsl:template match="ul">
<xsl:choose>
<xsl:when test="name(..)='li'">
<xsl:text disable-output-escaping="yes"></paragraph></xsl:text>
<list>
<xsl:apply-templates/>
</list>
</xsl:when>
<xsl:otherwise>
<list>
<xsl:apply-templates/>
</list></xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="li">
<item>
<xsl:text disable-output-escaping="yes"><paragraph></xsl:text>
<xsl:apply-templates/>
<xsl:text disable-output-escaping="yes"></paragraph></xsl:text>
</item>
</xsl:template>
I am getting the output almost as i would like but with extra closing paragraph element (</paragraph>) as following:
<list>
<item><paragraph>list item 1 </paragraph></item>
<item><paragraph>List Item 2 </paragraph><list>
<item><paragraph>List item 2.1 </paragraph></item>
<item><paragraph>List Item 2.2 </paragraph></item>
</list>
</paragraph></item>
<item><paragraph>List Item 3 </paragraph></item>
</list>
You should avoid using disable-output-escaping in this way. It is really not good practise, as you have seen.
What you can do, for your given XML, is use a template that matches text() and then wrap the paragraph around that:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">
<xsl:output method="xml" indent="yes""/>
<xsl:strip-space elements="*" />
<xsl:template match="ul">
<list>
<xsl:apply-templates/>
</list>
</xsl:template>
<xsl:template match="li">
<item>
<xsl:apply-templates/>
</item>
</xsl:template>
<xsl:template match="text()">
<paragraph>
<xsl:value-of select="normalize-space()" />
</paragraph>
</xsl:template>
</xsl:stylesheet>
Alternatively, if you could have mark-up in your text, like <li>list <b>item 1</b> </li>, and you wanted output like <paragraph>list<b>item 1</b></paragraph>, then you could make use of xsl:for-each-group.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">
<xsl:output method="xml" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="ul">
<list>
<xsl:apply-templates/>
</list>
</xsl:template>
<xsl:template match="li">
<item>
<xsl:for-each-group select="node()" group-ending-with="ul">
<paragraph>
<xsl:apply-templates select="current-group()[not(self::ul)]" />
</paragraph>
<xsl:apply-templates select="current-group()[self::ul]" />
</xsl:for-each-group>
</item>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="normalize-space()" />
</xsl:template>
</xsl:stylesheet>
I have the following XML file:
<li id="s9781452281988.n39.i34"><i>See also</i>
<a class="term-ref" id="s9781452281988.n39.i6525" href="#s9781452281988.n39.i1899">Emotion</a>;
<a class="term-ref" id="s9781452281988.n39.i6526" href="#s9781452281988.n39.i3312">Interpersonal conflict</a></li>
And I want the output to be the following:
<item>See also
<list rend="runon">
<item><term>Emotion</term></item>
<item><term>Interpersonal conflict</term></item>
</list>
</item>
Basically if I have multiple a[#class='term-ref'], the first instance should start the list rend="runon" and subsequent a[#class='term-ref'] should be included as item/term within the list.
The below was my try, but it is not working as I had hoped, and is closing the list before the second item/term (elements which are also not being output):
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
version="2.0">
<xsl:template match="li">
<xsl:element name="item">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="a[#class='term-ref'][1]">
<xsl:element name="list">
<xsl:attribute name="rend" select="'runon'"/>
<xsl:element name="item">
<xsl:element name="term">
<xsl:apply-templates/>
</xsl:element>
</xsl:element>
<xsl:if test="a[#class='term-ref'][position() >1]">
<xsl:element name="item">
<xsl:element name="term">
<xsl:apply-templates/>
</xsl:element>
</xsl:element>
</xsl:if>
</xsl:element>
</xsl:template>
<xsl:template match="li//text()">
<xsl:value-of select="translate(., '.,;', '')"/>
</xsl:template>
</xsl:stylesheet>
On the source, XML, the above stylesheet produces this output:
<item>See also
<list rend="runon">
<item><term>Emotion</term></item>
</list>
Interpersonal conflict</item>
Which is incorrect.
What am i doing wrong?
This short transformation (almost completely "push style", with no conditional instructions, no xsl:element and no unnecessary function calls like translate() or replace()):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="li">
<item><xsl:apply-templates/></item>
</xsl:template>
<xsl:template match="a[#class='term-ref'][1]">
<list rend="runon">
<xsl:apply-templates mode="group"
select="../a[#class='term-ref']"/>
</list>
</xsl:template>
<xsl:template match="a[#class='term-ref']" mode="group">
<item><term><xsl:apply-templates/></term></item>
</xsl:template>
<xsl:template match="a[#class='term-ref']|li/text()" priority="-1"/>
</xsl:stylesheet>
when applied on the provided XML document -- which is well-formed:
<li id="s9781452281988.n39.i34"><i>See also</i>
<a class="term-ref" id="s9781452281988.n39.i6525"
href="#s9781452281988.n39.i1899">Emotion</a>;
<a class="term-ref" id="s9781452281988.n39.i6526"
href="#s9781452281988.n39.i3312">Interpersonal conflict.</a>.
</li>
produces the wanted, correct result:
<item>See also<list rend="runon">
<item>
<term>Emotion</term>
</item>
<item>
<term>Interpersonal conflict.</term>
</item>
</list>
</item>
This should work...
XML Input (well-formed)
<doc>
<li id="s9781452281988.n39.i34"><i>See also</i>
<a class="term-ref" id="s9781452281988.n39.i6525" href="#s9781452281988.n39.i1899">Emotion</a>;
<a class="term-ref" id="s9781452281988.n39.i6526" href="#s9781452281988.n39.i3312">Interpersonal conflict.</a>.
</li>
</doc>
XSLT 2.0
<xsl:stylesheet version="2.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="li">
<item>
<xsl:apply-templates select="i/text()"/>
<xsl:if test="a">
<list rend="runon">
<xsl:apply-templates select="a"/>
</list>
</xsl:if>
</item>
</xsl:template>
<xsl:template match="a">
<item><term><xsl:apply-templates select="node()"/></term></item>
</xsl:template>
<xsl:template match="li//text()">
<xsl:value-of select="replace(.,'[.,;]','')"/>
</xsl:template>
</xsl:stylesheet>
Output
<doc>
<item>See also<list rend="runon">
<item>
<term>Emotion</term>
</item>
<item>
<term>Interpersonal conflict</term>
</item>
</list>
</item>
</doc>
This should do what you are looking to do:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:template match="li">
<xsl:element name="item">
<xsl:apply-templates select="node()" />
<xsl:apply-templates select="." mode="items" />
</xsl:element>
</xsl:template>
<xsl:template match="li//text()">
<xsl:value-of select="normalize-space(translate(., '.,;', ''))"/>
</xsl:template>
<xsl:template match="a[#class = 'term-ref']" />
<xsl:template match="node()" mode="items" />
<xsl:template match="li" mode="items">
<xsl:apply-templates mode="items" />
</xsl:template>
<xsl:template match="li[count(a[#class = 'term-ref']) > 1]" mode="items">
<list rend="runon">
<xsl:apply-templates select="a[#class = 'term-ref']" mode="items" />
</list>
</xsl:template>
<xsl:template match="a[#class = 'term-ref']" mode="items">
<item>
<term>
<xsl:value-of select="."/>
</term>
</item>
</xsl:template>
</xsl:stylesheet>
When run on your sample input, this produces:
<item>
See also<list rend="runon">
<item>
<term>Emotion</term>
</item>
<item>
<term>Interpersonal conflict</term>
</item>
</list>
</item>
When run on an input file with just one a.term-ref, this produces:
<item>
See also<item>
<term>Interpersonal conflict</term>
</item>
</item>
I could use some help.
Here is an illustrative example of my XML:
<items>
<item>
<name>Thing 1</name>
<colors>
<color>white</color>
<color>green</color>
</colors>
</item>
<item>
<name>Thing 2</name>
<colors>
<color>purple</color>
<color>white</color>
<color>black</color>
</colors>
</item>
<item>
<name>Thing 3</name>
<colors/>
</item>
</items>
A trimmed down example version of my xslt is the following:
<xsl:key name="myGrouping" use="colors/color" match="item"/>
<xsl:template match="/">
<xsl:apply-templates select="items" mode="groupingTemplate"/>
</xsl:template>
<xsl:template mode="groupingTemplate" match="items">
<xsl:for-each select="item[count(.|key('myGrouping',colors/color)[1])=1]">
<xsl:sort select="colors/color"/>
<xsl:if test="count(colors/color)>0">
<p><xsl:value-of select="colors/color"/></p>
<xsl:for-each select="key('myGrouping',colors/color)">
<xsl:sort select="name"/>
<li><xsl:value-of select="name"/></li>
</xsl:for-each>
</xsl:if>
</xsl:for-each>
</xsl:template>
What I want to do is group in XSLT 1.0 (using the Muenchian method) on the color nodes, so my html output will be:
<p>black</p>
<li>Thing 2</li>
<p>green</p>
<li>Thing 1</li>
<p>purple</p>
<li>Thing 2</li>
<p>white<p>
<li>Thing 1</li>
<li>Thing 2</li>
So far my code can do this but only picks up the first entry. In other words, in the above example my output currently is:
<p>white</p>
<li>Thing 1</li>
<li>Thing 2</li>
Help on a solution and an explanation of why this happens would be greatly appreciated.
Thanks!
Jeff
Figured it out after much hair pulling... the following xsl does the trick
<xsl:key name="byColor" use="." match="item/colors/color"/>
<xsl:template match="/">
<xsl:apply-templates select="items" mode="groupingTemplate"/>
</xsl:template>
<xsl:template mode="groupingTemplate" match="items">
<xsl:for-each select="item/colors/color[count(.|key('byColor',.)[1])=1]">
<xsl:sort select="."/>
<xsl:if test="count(.)>0">
<p><xsl:value-of select="."/></p>
<xsl:for-each select="key('byColor',.)">
<xsl:sort select="../../name"/>
<li><xsl:value-of select="../../name"/></li>
</xsl:for-each>
</xsl:if>
</xsl:for-each>
</xsl:template>
Sorry if my explanation in the last 'question' was a little rough, I'll give it another shot.I'm really new to xsl.
Through a series of substrings I have extracted the date field by storing it in a variable called $releasedate, I output this by using <xsl:value-of select="$releasedate" /> which would display dd/mm/yyyy e.g. 29/03/2010 in a 'for each' loop as shown below.
My xml is structured similar to below,
<item>
<title>Title Goes Here</title>
<description>test goes here.....<b>Release Date:22/20/2010</b> more text</description>
</item>
<item>
<title>Title Goes Here</title>
<description>More test goes here.....<b>Release Date:22/20/2010</b> more text</description>
</item>
I would like to be able to sort by the value of what is stored in $releasedate.
I think maybe it would look like this below
<xsl:sort select="$releasedate" order="descending" />
I hope this makes a little more sense, my appologies again for my lack of knowledge in this.
Below is my xsl structure
<xsl:for-each select="item">
<xsl:sort select="pubDate"/>
<xsl:if test="position() < 5 ">
<!-- grabs items 1 to 5 -->
<xsl:variable name = "releasedatestart" >
<xsl:value-of select="substring-after(description,'Release Date:</b>')"/>
</xsl:variable>
<xsl:variable name = "releasedate" >
<xsl:value-of select="substring-before($releasedatestart,'</div>')"/>
</xsl:variable>
<xsl:variable name = "displaynamestart" >
<xsl:value-of select="substring-after(description,'Display Name:</b>')"/>
</xsl:variable>
<xsl:variable name = "displayname" >
<xsl:value-of select="substring-before($displaynamestart,'</div>')"/>
</xsl:variable>
<div style="margin:0px;background-color:#f2eff3;border:1px solid #ded6df;">
<xsl:variable name = "start" >
<xsl:value-of select="substring-after(description,'Description:</b>')"/>
</xsl:variable>
<xsl:variable name = "title" >
<xsl:value-of select="substring($start,0,100)"/>
</xsl:variable>
<xsl:variable name = "pagelink" >
<xsl:value-of select="substring(link,1,61)"/>
</xsl:variable>
<xsl:variable name = "pageurl" >
<xsl:value-of select="$pagelink"/>
<xsl:value-of select="title"/>.aspx
</xsl:variable>
<div style="min-height:50px;">
<div class="column" id="feeddate">
<xsl:value-of select="$releasedate" />
</div>
<div class="column" id="displayname">
<xsl:value-of select="$displayname" />
</div>
<div class="column" id="feedtitle">
<xsl:value-of select="$title" disable-output-escaping="yes"/>
<a>
<xsl:attribute name="href">
<xsl:value-of select="$pageurl" />
</xsl:attribute>
..Read More
</a>
</div>
</div>
</div>
</xsl:if>
</xsl:for-each>
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="items">
<items>
<xsl:apply-templates>
<xsl:sort select=
"substring(substring-after(description, 'Release Date:'),7)"
data-type="number" order="descending"/>
<xsl:sort select=
"substring(substring-after(description, 'Release Date:'),4,2)"
data-type="number" order="descending"/>
<xsl:sort select=
"substring(substring-after(description, 'Release Date:'),1,2)"
data-type="number" order="descending"/>
</xsl:apply-templates>
</items>
</xsl:template>
</xsl:stylesheet>
when applied on this document:
<items>
<item>
<title>Title1</title>
<description>text1 .....
<b>Release Date:22/12/2010</b> more text
</description>
</item>
<item>
<title>Title Goes Here</title>
<description>More test goes here.....
<b>Release Date:23/12/2010</b> more text
</description>
</item>
</items>
produces the wanted, sorted result:
<items>
<item>
<title>Title Goes Here</title>
<description>More test goes here.....
<b>Release Date:23/12/2010</b> more text
</description>
</item>
<item>
<title>Title1</title>
<description>text1 .....
<b>Release Date:22/12/2010</b> more text
</description>
</item>
</items>
Explanation:
Use of substring-after() and substring().
Multiple <xsl:sort> children of <xsl:apply-templates>.
Identity transform.