I am using XSLT and XML to generated my HTML.
I have below XML.
1) destinationTabs.xml
<?xml version="1.0"?>
<list type="Tabs">
<tab id="594978-64" title="Overview" url="/destinations_offers/destinations/asiapacific/india/newdelhi/index.aspx" />
<tab id="597468-64" title="Guide" url="/destinations_offers/destinations/asiapacific/india/newdelhi/guide.aspx" />
<tab id="597348-64" title="Flight Schedule" url="/destinations_offers/destinations/asiapacific/india/newdelhi/flightschedule.aspx" />
<tab id="597238-64" title="Special Offers" url="/destinations_offers/destinations/asiapacific/india/newdelhi/specialOffers.aspx" />
<tab id="597243-64" title="Photos" url="/destinations_offers/destinations/asiapacific/india/newdelhi/photo.aspx" />
</list>
Now I want to generate below HTML using XSLT.
<ul class="tabHead tabs-nav">
<li class="tabLeftEnd"></li>
<li class="tabs-selected" id="tab-1"><span>Overview</span></li>
<li id="tab-2"><span>Guide</span></li>
<li id="tab-3"><span>Flight Schedules</span></li>
<li id="tab-4"><span>Special Offers</span></li>
<li id="tab-5"><span>Photos</span></li>
<li class="tabRightEnd"></li>
</ul>
Please note that these are the tabs which depends on the no of the tab ID in destinationTabs.xml and whenever any user click the desired tab a new page is called and it will selected too.
There is a class "tabs-selected" which is flowing on the selection of each tab.
Please suggest!
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pSelectedNo" select="1"/>
<xsl:template match="/*">
<ul class="tabHead tabs-nav">
<li class="tabLeftEnd"></li>
<xsl:apply-templates/>
<li class="tabRightEnd"></li>
</ul>
</xsl:template>
<xsl:template match="tab">
<li id="tab-{position()}">
<xsl:if test="position() = $pSelectedNo">
<xsl:attribute name="class">tabs-selected</xsl:attribute>
</xsl:if>
<span><xsl:value-of select="#title"/></span>
</li>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<list type="Tabs">
<tab id="594978-64" title="Overview"
url="/destinations_offers/destinations/asiapacific/india/newdelhi/index.aspx" />
<tab id="597468-64" title="Guide"
url="/destinations_offers/destinations/asiapacific/india/newdelhi/guide.aspx" />
<tab id="597348-64" title="Flight Schedule"
url="/destinations_offers/destinations/asiapacific/india/newdelhi/flightschedule.aspx" />
<tab id="597238-64" title="Special Offers"
url="/destinations_offers/destinations/asiapacific/india/newdelhi/specialOffers.aspx" />
<tab id="597243-64" title="Photos"
url="/destinations_offers/destinations/asiapacific/india/newdelhi/photo.aspx" />
</list>
produces the wanted, correct result:
<ul class="tabHead tabs-nav">
<li class="tabLeftEnd"></li>
<li id="tab-1" class="tabs-selected"><span>Overview</span></li>
<li id="tab-2"><span>Guide</span></li>
<li id="tab-3"><span>Flight Schedule</span></li>
<li id="tab-4"><span>Special Offers</span></li>
<li id="tab-5"><span>Photos</span></li>
<li class="tabRightEnd"></li>
</ul>
This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="list">
<ul class="tabHead tabs-nav">
<li class="tabLeftEnd"></li>
<xsl:apply-templates/>
<li class="tabRightEnd"></li>
</ul>
</xsl:template>
<xsl:template match="tab">
<li id="tab-{position()}">
<a href="{#url}">
<span>
<xsl:value-of select="#title"/>
</span>
</a>
</li>
</xsl:template>
</xsl:stylesheet>
Output:
<ul class="tabHead tabs-nav">
<li class="tabLeftEnd"></li>
<li id="tab-1">
<a href="/destinations_offers/destinations/asiapacific/india/newdelhi/index.aspx">
<span>Overview</span>
</a>
</li>
<li id="tab-2">
<a href="/destinations_offers/destinations/asiapacific/india/newdelhi/guide.aspx">
<span>Guide</span>
</a>
</li>
<li id="tab-3">
<a href="/destinations_offers/destinations/asiapacific/india/newdelhi/flightschedule.aspx">
<span>Flight Schedule</span>
</a>
</li>
<li id="tab-4">
<a href="/destinations_offers/destinations/asiapacific/india/newdelhi/specialOffers.aspx">
<span>Special Offers</span>
</a>
</li>
<li id="tab-5">
<a href="/destinations_offers/destinations/asiapacific/india/newdelhi/photo.aspx">
<span>Photos</span>
</a>
</li>
<li class="tabRightEnd"></li>
</ul>
Note: About #class: You need to pass a parameter to the stylesheet, by javascript or by metadata in the retrived resource.
Related
I'm trying to create list elements in the below structure using xsl:apply-templates. Is it possible to achieve the below output without using xsl:for-each?
i am able to acheive thee below structure with xsl:for-each but would like to know if it is possible with xsl:apply-templates.
Below is my XML
<Properties>
<Root>
<group-container>
<group-title>
<title-name>Packs1</title-name>
<title-sub-links>
<subtitle-name>sub1</subtitle-name>
</title-sub-links>
<title-sub-links>
<subtitle-name>sub2</subtitle-name>
</title-sub-links>
</group-title>
<group-title>
<title-name>Packs2</title-name>
<title-sub-links>
<subtitle-name>abc</subtitle-name>
</title-sub-links>
<title-sub-links>
<subtitle-name>xyz</subtitle-name>
</title-sub-links>
</group-title>
</group-container>
<group-title>
<title-name>link title 1</title-name>
</group-title>
<group-title>
<title-name>link xyz</title-name>
</group-title>
</Root>
</Properties>
XSL
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<div class="col-9 tab">
<ul>
<xsl:apply-templates select = "/Properties/Root/group-container/group-title"/>
<xsl:apply-templates select = "/Properties/Root/group-container/group-title/title-sub-links"/>
</ul>
</div>
</xsl:template>
<xsl:template match = "group-title">
<li>
<xsl:value-of select="title-name"/>
</li>
</xsl:template>
<xsl:template match = "title-sub-links">
<li>
<xsl:value-of select="subtitle-name"/>
</li>
</xsl:template>
</xsl:stylesheet>
Output received
<div class="col-9 tab">
<ul>
<li>Packs1</li>
<li>Packs2</li>
<li>sub1</li>
<li>sub2</li>
<li>abc</li>
<li>xyz</li>
</ul>
</div>
Expected output
<div class="col-9 tab">
<ul>
<li>Packs1</li>
<li>sub1</li>
<li>sub2</li>
</ul>
<ul>
<li>Packs2</li>
<li>abc</li>
<li>xyz</li>
</ul>
</div>
I think (!) you want to do:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<div class="col-9 tab">
<xsl:apply-templates select = "/Properties/Root/group-container/group-title"/>
</div>
</xsl:template>
<xsl:template match = "group-title">
<ul>
<li>
<xsl:value-of select="title-name"/>
</li>
<xsl:apply-templates select = "title-sub-links"/>
</ul>
</xsl:template>
<xsl:template match = "title-sub-links">
<li>
<xsl:value-of select="subtitle-name"/>
</li>
</xsl:template>
</xsl:stylesheet>
This should be so simple! I have an XML document containing a menu hierarchy:
<?xml version="1.0" encoding="utf-8"?>
<menu page_id="18" language="en-GB">
<item id="1" child_of="0">
<menu_order>1</menu_order>
<title><![CDATA[Home]]></title>
</item>
<item id="18" child_of="0">
<title><![CDATA[Page One]]></title>
<submenu child_of="18">
<item id="20" child_of="18">
<title><![CDATA[Sub Menu One]]></title>
<submenu child_of="20">
<item id="26" child_of="20">
<title><![CDATA[SubMenu 1-1]]></title>
</item>
<item id="27" child_of="20">
<title><![CDATA[SubMenu 1-2]]></title>
</item>
</submenu>
</item>
<item id="21" child_of="18">
<title><![CDATA[Sub Menu Two]]></title>
<submenu child_of="21">
<item id="28" child_of="21">
<title><![CDATA[SubMenu 2-1]]></title>
</item>
<item id="29" child_of="21">
<title><![CDATA[SubMenu 2-2]]></title>
<submenu child_of="29">
<item id="32" child_of="29">
<title><![CDATA[SubMenu 2-2-1]]></title>
</item>
<item id="33" child_of="29">
<title><![CDATA[SubMenu 2-2-2]]></title>
</item>
</submenu>
</item>
<item id="30" child_of="21">
<title><![CDATA[SubMenu 2-3]]></title>
</item>
<item id="31" child_of="21">
<title><![CDATA[SubMenu 2-4]]></title>
</item>
</submenu>
</item>
<item id="22" child_of="18">
<title><![CDATA[Sub Menu Three]]></title>
</item>
</submenu>
</item>
<item id="19" child_of="0">
<title><![CDATA[Page Two]]></title>
</item>
</menu>
I need to transform it into an HTML list where (1) if the active/clicked menu item has a submenu, it shows the submenu (only the menu's child items) underneath it. For example, clicking on item id = "18" should give me this:
<ul>
<li id="1">Home</li>
<li id="18">Page One
<ul>
<li id="20">Sub Menu One</li>
<li id="21">Sub Menu Two</li>
<li id="22">Sub Menu Three</li>
</ul>
</li>
<li id="19">Page Two</li>
</ul>
Or (2) if the active menu item has ancestors, it renders all of its immediate siblings and ancestors in the structure. For example, clicking item id="33" should give me this:
<ul>
<li id="1">Home</li>
<li id="18">Page One<ul>
<li id="20">Sub Menu One</li>
<li id="21">Sub Menu Two<ul>
<li id="28">SubMenu 2-1</li>
<li id="29">SubMenu 2-2
<ul>
<li id="32">SubMenu 2-2-1</li>
<li id="33">SubMenu 2-2-2</li>
</ul>
</li>
<li id="30">SubMenu 2-3</li>
<li id="31">SubMenu 2-4</li>
</ul>
</li>
<li id="22">Sub Menu Three</li>
</ul>
</li>
<li id="19">Page Two</li>
</ul>
My trouble is that that my XSL style sheet produces a list with TWO ancestors submenus! Please help ... I've been at this for HOURS! Here's my XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes"/>
<!-- The DOM ID of the active menu item -->
<xsl:param name="activeItemID"/>
<xsl:template match="text()">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="menu">
<ul>
<xsl:choose>
<xsl:when test="//item[#id = $activeItemID]/#child_of = '0'">
<xsl:choose>
<xsl:when test="//submenu[#child_of = $activeItemID]/node()">
<xsl:apply-templates
select="//item[#child_of = '0'] | //submenu[#child_of = $activeItemID]"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="//item[#child_of = '0']"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="//submenu[#child_of = $activeItemID]/node()">
<xsl:apply-templates
select="//item[#child_of = '0'] | //submenu[#child_of = $activeItemID]/ancestor-or-self::submenu/item"
/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates
select="//item[#child_of = '0'] | //item[#id = $activeItemID]/ancestor-or-self::submenu"
/>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</ul>
</xsl:template>
<xsl:template match="item">
<li>
<xsl:value-of select="title" disable-output-escaping="no"/>
<xsl:apply-templates select="submenu[#child_of = $activeItemID]"/>
</li>
</xsl:template>
<xsl:template match="submenu">
<ul>
<xsl:apply-templates select="item[#child_of = $activeItemID]">
<xsl:sort select="menu_order" data-type="number"/>
</xsl:apply-templates>
</ul>
</xsl:template>
</xsl:stylesheet>
This XSLT should do what you are looking to do:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes"/>
<!-- The DOM ID of the active menu item -->
<xsl:param name="activeItemID" />
<xsl:template match="menu | submenu">
<ul>
<xsl:apply-templates select="item" />
</ul>
</xsl:template>
<xsl:template match="item">
<li id="{#id}">
<xsl:value-of select="title" />
<xsl:apply-templates select="submenu[..//#id = $activeItemID]"/>
</li>
</xsl:template>
</xsl:stylesheet>
When run on your sample input with the parameter value as 18, it produces:
<ul>
<li id="1">Home</li>
<li id="18">
Page One<ul>
<li id="20">Sub Menu One</li>
<li id="21">Sub Menu Two</li>
<li id="22">Sub Menu Three</li>
</ul>
</li>
<li id="19">Page Two</li>
</ul>
When run with the parameter value as 33, it produces:
<ul>
<li id="1">Home</li>
<li id="18">
Page One<ul>
<li id="20">Sub Menu One</li>
<li id="21">
Sub Menu Two<ul>
<li id="28">SubMenu 2-1</li>
<li id="29">
SubMenu 2-2<ul>
<li id="32">SubMenu 2-2-1</li>
<li id="33">SubMenu 2-2-2</li>
</ul>
</li>
<li id="30">SubMenu 2-3</li>
<li id="31">SubMenu 2-4</li>
</ul>
</li>
<li id="22">Sub Menu Three</li>
</ul>
</li>
<li id="19">Page Two</li>
</ul>
Your XSLT had some sorting logic, but the element that was being used for the sort only seemed to be present in the source XML in one place. Was that something you wanted to use? Did you omit most of the menu_order elements from the source XML for simplicity?
Below I created a simplyfied XML example of what my xml looks like. I have an attribute which contains a number that I would like to watch. It's sort of a counter and during the transformation I would like to add something whenever the counter ++.
The problem is the number of levels in my xml file. Here I only made three but I actually have like 8 or maybe even more. I need to find a way to compare the current node against the previous one (or vice versa) but with the levels taken into account. So for instance in the example below the lvl2 node with the id of 4 needs to be compared with the lvl3 node with id 3 simply to find out if the id attribute has been raised.
xml:
<lvl1 id="1">
<lvl2 id="1">
<lvl3 id="1"></lvl3>
<lvl3 id ="2"></lvl3>
</lvl2>
<lvl2 id="2">
<lvl3 id="3"></lvl3>
</lvl2>
<lvl2 id="4"></lvl2>
</lvl1>
Since global counter variables are out of the question with xslt Im currently out of ideas and can't seem to find any here or anywhere else..
the output would be something like:
<ul>
<div>id 1</div>
<li>
<ul>
<li>
<ul>
<li></li>
<div>id 2</div>
<li></li>
</ul>
</li>
<li>
<ul>
<div>id 3</div>
<li></li>
</ul>
</li>
<div>id 4</div>
<li></li>
</ul>
</li>
here the stylesheet which transforms the xml into the html output but without the divs:
<?xml version="1.0" encoding="UTF-8"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<ul>
<xsl:value-of select="#id"/>
<xsl:apply-templates select="lvl1"/>
</ul>
</xsl:template>
<xsl:template match="lvl1">
<li class="{#id}">
lvl 1
<ul>
<xsl:apply-templates select="lvl2"/>
</ul>
</li>
</xsl:template>
<xsl:template match="lvl2">
<li class="{#id}">lvl 2
<ul>
<xsl:apply-templates select="lvl3"/>
</ul>
</li>
</xsl:template>
<xsl:template match="lvl3">
<li class="{#id}">lvl 3
</li>
</xsl:template>
If you apply this XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<xsl:template match='/'>
<ul>
<xsl:apply-templates select='*' />
</ul>
</xsl:template>
<xsl:template match="*">
<li>
<div>
<xsl:value-of select='#id' />
<xsl:if test='count(*)>0'>
<ul>
<xsl:apply-templates select='*' />
</ul>
</xsl:if>
</div>
</li>
</xsl:template>
</xsl:stylesheet>
you get something that may be what you ask:
<ul>
<li>
<div>1<ul>
<li>
<div>1<ul>
<li>
<div>1</div>
</li>
<li>
<div>2</div>
</li>
</ul>
</div>
</li>
<li>
<div>2<ul>
<li>
<div>3</div>
</li>
</ul>
</div>
</li>
<li>
<div>4</div>
</li>
</ul>
</div>
</li>
</ul>
The question is a bit more complex than it seems, as preceding:: does not always work here to find last #id before current element. You need also check #id on parent and consider root position as well. This code produces the desired result with the XML given in the question. See my comments inside.
<xsl:template match="/">
<ul>
<xsl:apply-templates/>
</ul>
</xsl:template>
<xsl:template match="*">
<!-- get preceding (or parent) id -->
<xsl:variable name="lastId">
<xsl:choose>
<!-- try to get id from first preceding element -->
<xsl:when test="preceding::*[1]/#id">
<xsl:value-of select="preceding::*[1]/#id"/>
</xsl:when>
<!-- there could still be ids in parent elements -->
<xsl:when test="../#id">
<xsl:value-of select="../#id"/>
</xsl:when>
<!-- or this is the root element -->
<xsl:otherwise>
<xsl:value-of select="0"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- now compare current and last id -->
<xsl:if test="#id > $lastId">
<div>
<xsl:text>id </xsl:text>
<xsl:value-of select="#id"/>
</div>
</xsl:if>
<!-- create list item -->
<li>
<!-- check for subelements -->
<xsl:if test="*">
<ul>
<xsl:apply-templates/>
</ul>
</xsl:if>
</li>
</xsl:template>
Depending on your real scenario, you might want to limit match="*" to match="*[starts-with(local-name(),'lvl')]", as proposed in a comment by LarsH.
I need some clarification on XSLT how to do the following in XSLT.
I have the source file as this.
<Data>
<additem>
<choice>desc</choice>
<sectiontext>
<a title="google" href="http://google.com" xmlns="http://www.w3.org/1999/xhtml">
<strong>Sample Text</strong>
<ul>
<li><em>aa</em></li>
<li><em>bb</em></li>
<li><em>cc</em></li>
</ul>
</a>
</sectiontext>
</additem>
<additem>
<choice>image</choice>
<files>
<a xmlns="http://www.w3.org/1999/xhtml" title="image location" href="xyz:12-2022">
<img alt="No Image" title="No Image" xlink:href="some image path" xmlns:xlink="http://www.w3.org/1999/xlink"></img>
</a>
</files>
</additem>
<additem>
<choice>Paragraph</choice>
<sectiontext>
<a title="google" href="http://google.com" xmlns="http://www.w3.org/1999/xhtml">
<strong>Sample Text</strong>
<ul>
<li><em>aa</em></li>
<li><em>bb</em></li>
<li><em>cc</em></li>
</ul>
</a>
hello alll
</sectiontext>
</additem>
</Data>
Output:
<Information>
<Section>
<text>
<strong>Sample Text</strong>
<ul>
<li><em>aa</em></li>
<li><em>bb</em></li>
<li><em>cc</em></li>
</ul>
</text>
<link external="http://google.com" title="google"></link>
</Section>
<picture>
<image src="some image path" altText="No Image">
<link local="xyz:12-2022" title="image location"></link>
</image>
</picture>
<Body>
<text>
<hyperlink>
<text>
<strong>Sample Text</strong>
<ul>
<li><em>aa</em></li>
<li><em>bb</em></li>
<li><em>cc</em></li>
</ul>
</text>
<link external="http://google.com" title="google"></link>
</hyperlink>
hello alll
</text>
</Body>
</Information>
Rules:
1.Depending on the choice in addItem/choice, we need to create the tag.
choice -- Desc
desc -- Section
image -- picture
Paragraph----Body
2.Handling tag
Currently tag is wrapping for some other tag.
A.If any element has only <a> in it. For example in the source,
Code in the source:
<sectiontext>
<a title="google" href="http://google.com" xmlns="http://www.w3.org/1999/xhtml">
<strong>Sample Text</strong>
<ul>
<li><em>aa</em></li>
<li><em>bb</em></li>
<li><em>cc</em></li>
</ul>
</a>
</sectiontext>
Need to seperate that tag and create a tag
i. if the "href" in attribute in <a> tag starts with "xyz:" need to add it as "local" attribute in <link> element
ii. If the "href" in the attribute <a> tag starts with "http" need to add it as "external" attribute in <link> element.
ii. "title" attribute in <a> tag remains same in <link>
B.if any element has any other element other than <a> tag.
Code in the source:
<sectiontext>
<a title="google" href="http://google.com" xmlns="http://www.w3.org/1999/xhtml">
<strong>Sample Text</strong>
<ul>
<li><em>aa</em></li>
<li><em>bb</em></li>
<li><em>cc</em></li>
</ul>
</a>
hello alll
</sectiontext>
I need to get the out put as
<text>
<hyperlink>
<text>
<strong>Sample Text</strong>
<ul>
<li><em>aa</em></li>
<li><em>bb</em></li>
<li><em>cc</em></li>
</ul>
</text>
<link external="http://google.com" title="google"></link>
</hyperlink>
hello alll
</text>
Rules:
i. In the all the text inside the <a> tag have to come under the <inlinelink> tag as shown above.
Can any one help how it can be done.
Thank you.
This XSLT 1.0 style-sheet ...
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
xmlns:xlink="http://www.w3.org/1999/xlink" >
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<Information>
<xsl:apply-templates select="Data/additem"/>
</Information>
</xsl:template>
<xsl:template match="xhtml:a[../../self::additem]">
<link title="{#title}">
<xsl:if test="starts-with(#href,'http')">
<xsl:attribute name="external"><xsl:value-of select="#href" /></xsl:attribute>
</xsl:if>
<xsl:if test="starts-with(#href,'xyz:')">
<xsl:attribute name="local"><xsl:value-of select="#href" /></xsl:attribute>
</xsl:if>
</link>
</xsl:template>
<xsl:template match="additem[choice='desc']">
<Section>
<text>
<xsl:apply-templates select="sectiontext/xhtml:a/*" />
</text>
<xsl:apply-templates select="sectiontext/xhtml:a" />
</Section>
</xsl:template>
<xsl:template match="additem[choice='image']">
<picture>
<image src="{files/xhtml:a/xhtml:img/#xlink:href}" altText="{files/xhtml:a/xhtml:img/#alt}">
<apply-templates select="files/xhtml:a" />
</image>
</picture>
</xsl:template>
<xsl:template match="additem[choice='Paragraph']">
<Body>
<text>
<hyperlink>
<text>
<xsl:apply-templates select="sectiontext/xhtml:a/*" />
</text>
<xsl:apply-templates select="sectiontext/xhtml:a" />
</hyperlink>
<xsl:apply-templates select="sectiontext/node()[not(self::xhtml:a)]" />
</text>
</Body>
</xsl:template>
</xsl:stylesheet>
... will transform your specified input document into this output document ...
<?xml version="1.0" encoding="utf-8"?>
<Information xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xlink="http://www.w3.org/1999/xlink">
<Section>
<text>
<strong xmlns="http://www.w3.org/1999/xhtml">Sample Text</strong>
<ul xmlns="http://www.w3.org/1999/xhtml">
<li><em>aa</em></li>
<li><em>bb</em></li>
<li><em>cc</em></li>
</ul>
</text>
<link title="google" external="http://google.com" />
</Section>
<picture>
<image src="some image path" altText="No Image">
<apply-templates select="files/xhtml:a" />
</image>
</picture>
<Body>
<text>
<hyperlink>
<text>
<strong xmlns="http://www.w3.org/1999/xhtml">Sample Text</strong>
<ul xmlns="http://www.w3.org/1999/xhtml">
<li><em>aa</em></li>
<li><em>bb</em></li>
<li><em>cc</em></li>
</ul>
</text>
<link title="google" external="http://google.com" />
</hyperlink>
hello alll
</text>
</Body>
</Information>
Explanation
Each of your rules was taken one by one and used to build a template, starting with the identification of the match condition.
I'm new to XSLT. I know I need to use xsl:for-each-group, but I can't figure out anything other than a basic list. Would some sort of recursion work better? Any XSLT 1.0 or 2.0 solution would be fine.
Below is the example XML. Note the most important attribute for organizing data into a tree structure is #taxonomy. Other attributes #taxonomyName and #level are provided as optional helper attributes.
<?xml version="1.0" encoding="utf-8"?>
<documents>
<document level="0" title="Root document test" taxonomy="" taxonomyName="" />
<document level="1" title="Level one document test" taxonomy="\CategoryI" taxonomyName="CategoryI" />
<document level="1" title="Level one document test #2" taxonomy="\CategoryII" taxonomyName="CategoryII" />
<document level="2" title="Level two document test" taxonomy="\CategoryII\SubcategoryA" taxonomyName="SubcategoryA" />
<document level="2" title="Level two document test #2" taxonomy="\CategoryII\SubcategoryA" taxonomyName="SubcategoryA" />
<document level="3" title="Level three document test" taxonomy="\CategoryII\SubcategoryA\Microcategory1" taxonomyName="Microcategory1" />
<document level="2" title="Level two, no level one test" taxonomy="\CategoryIII\SubcategoryZ" taxonomyName="SubcategoryZ" />
</documents>
Here's the expected output. (Please note that indenting is not necessary in the output. I've done it here for readability.)
<ul>
<li>Root document test</li>
<li>CategoryI
<ul>
<li>Level one document test</li>
</ul>
</li>
<li>CategoryII
<ul>
<li>Level one document test #2</li>
<li>SubcategoryA
<ul>
<li>Level two document test</li>
<li>Level two document test #2</li>
<li>Microcategory1
<ul>
<li>Level three document test</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>CategoryIII
<ul>
<li>SubcategoryZ
<ul>
<li>Level two, no subcategory test</li>
</ul>
</li>
</ul>
</li>
</ul>
Here's the best I can do.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:key name="contacts-by-taxonomy" match="document" use="#taxonomy" />
<xsl:template match="documents">
<ul>
<xsl:for-each-group select="document" group-by="#taxonomy">
<xsl:sort select="#taxonomy" />
<li>
<h3><xsl:value-of select="current-grouping-key()"/></h3>
<ul>
<xsl:for-each select="current-group()">
<li><xsl:value-of select="#title"/></li>
</xsl:for-each>
</ul>
</li>
</xsl:for-each-group>
</ul>
</xsl:template>
</xsl:stylesheet>
I'll keep chugging away at it, but would be eternally grateful if someone could throw me a life jacket. Thanks!
OK, here's my solution at last. :-) Basically it recurses through the tree, and at each level, it does a for-each-group group-by="the next level of #taxonomy".
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="documents">
<ul>
<xsl:call-template name="tree-depth-n">
<xsl:with-param name="population" select="document"/>
<xsl:with-param name="depth" select="0"/>
<xsl:with-param name="taxonomy-so-far" select="''"/>
</xsl:call-template>
</ul>
</xsl:template>
<!-- This template is called with a population that are all descendants
of the same ancestors up to level n. -->
<xsl:template name="tree-depth-n">
<xsl:param name="depth" required="yes"/>
<xsl:param name="population" required="yes"/>
<xsl:param name="taxonomy-so-far" required="yes"/>
<!-- output a <li> for each document that is a leaf at this level,
and a <li> for each sub-taxon of this level. -->
<xsl:for-each-group select="$population"
group-by="string(tokenize(#taxonomy, '\\')[$depth + 2])">
<xsl:sort select="#taxonomy" />
<xsl:choose>
<!-- process documents at this level. -->
<xsl:when test="current-grouping-key() = ''">
<xsl:for-each select="current-group()">
<li><xsl:value-of select="#title"/></li>
</xsl:for-each>
</xsl:when>
<!-- process subcategories -->
<xsl:otherwise>
<li>
<h3><xsl:value-of select="current-grouping-key()"/></h3>
<ul>
<!-- recurse -->
<xsl:call-template name="tree-depth-n">
<xsl:with-param name="population" select="current-group()"/>
<xsl:with-param name="depth" select="$depth + 1"/>
<xsl:with-param name="taxonomy-so-far"
select="concat($taxonomy-so-far, '\\', current-grouping-key())"/>
</xsl:call-template>
</ul>
</li>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
With the given input, the output is:
<ul>
<li>Root document test</li>
<li>
<h3>CategoryI</h3>
<ul>
<li>Level one document test</li>
</ul>
</li>
<li>
<h3>CategoryII</h3>
<ul>
<li>Level one document test #2</li>
<li>
<h3>SubcategoryA</h3>
<ul>
<li>Level two document test</li>
<li>Level two document test #2</li>
<li>
<h3>Microcategory1</h3>
<ul>
<li>Level three document test</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>
<h3>CategoryIII</h3>
<ul>
<li>
<h3>SubcategoryZ</h3>
<ul>
<li>Level two, no level one test</li>
</ul>
</li>
</ul>
</li>
</ul>
Which I believe is what you wanted. (I put <h3>s in there as you did in your XSL attempt, for the category names and not for the document titles.)