XSL group by XML Attribute - xslt

I want to create a list by grouping my xml entries by attribute using.
Here's my xml:
<Content>
<contenItem ProductType="Breakfast" name="Eggs" />
<contenItem ProductType="Breakfast" name="Bacon" />
<contenItem ProductType="Lunch" name="Fish" />
<contenItem ProductType="Dinner" name="Steak" />
</Content>
I'm trying to get this result but couldn't figure out how
<ul>
<li>Breakfast
<ul>
<li>Eggs</li>
<li>Bacon</li>
</ul>
</li>
<li>Lunch
<ul>
<li>Fish</li>
</ul>
</li>
<li>Dinner
<ul>
<li>Steak</li>
</ul>
</li>
</ul>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" doctype-system="about:legacy-compat" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:key name="kProductType" match="contenItem" use="#ProductType" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<ul>
<xsl:apply-templates select="contenItem[
generate-id() = generate-id(key('kProductType',#ProductType)[1])]" />
</ul>
</xsl:template>
<xsl:template match="contenItem">
<li>
<xsl:value-of select="#ProductType" />
<ul>
<xsl:apply-templates select="key('kProductType',#ProductType)" mode="sublist"/>
</ul>
</li>
</xsl:template>
<xsl:template match="contenItem" mode="sublist">
<li>
<xsl:value-of select="#name" />
</li>
</xsl:template>
</xsl:stylesheet>

Related

How do I add .01 to the end of a number using XSLT?

I need to add .01 to the entry in this line of code in my XSLT script <xsl:apply-templates select="descendant::xhtml:span[#property = 'atom:content-item-name']"/>. This number appears twice in my output. I only need the .01 on the second entry. How would I do that? I guess it would be in effect a counter because if there are 2 questions I'd need it to number as .02, etc.
This is the full XSLT script:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:math="http://exslt.org/math"
xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
xmlns:mml="http://www.w3.org/1998/Math/MathML"
xmlns="http://www.w3.org/1999/xhtml"
exclude-result-prefixes="xs math xd xhtml mml"
version="3.0">
<xsl:output encoding="UTF-8" include-content-type="no" indent="no" method="xhtml" omit-xml-declaration="yes"/>
<!-- attributes, commments, processing instructions, text: copy as is -->
<xsl:template match="#*|comment()|processing-instruction()|text()">
<xsl:copy-of select="."/>
</xsl:template>
<!-- elements: create a new element with the same name, but no namespace -->
<xsl:template match="*">
<xsl:param name="content-item-name"/>
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#*|node()">
<xsl:with-param name="content-item-name" select="$content-item-name"/>
</xsl:apply-templates>
</xsl:element>
</xsl:template>
<!-- process root -->
<xsl:template match="xhtml:html">
<xsl:text disable-output-escaping="yes"><!DOCTYPE html></xsl:text>
<xsl:sequence select="'
'"/>
<html>
<xsl:apply-templates select="#* | node()"/>
</html>
</xsl:template>
<!-- process item, pass content item name variable -->
<xsl:template match="xhtml:li[#property='ktp:question']">
<li>
<xsl:apply-templates select="#*|node()">
<xsl:with-param name="content-item-name" select="descendant::xhtml:span[#property='atom:content-item-name']/#data-value"/>
</xsl:apply-templates>
</li>
</xsl:template>
<!-- branching point for any interaction type-based updates -->
<xsl:template match="xhtml:li[#property='ktp:question']">
<li>
<xsl:apply-templates select="#*|node()">
<xsl:with-param name="interactionType" select="//xhtml:span[#property='ktp:interactionType']"/>
</xsl:apply-templates>
</li>
</xsl:template>
<!-- Change content -->
<xsl:template match="xhtml:ol[#class='ktp-question-set']">
<xsl:variable name="content-item-name" select="xhtml:span[#property='atom:content-item-name']/#data-value"/>
<xsl:variable name="feedbackPara"
select="xhtml:section[#property = 'ktp:stimulus']"/>
<ol property="ktp:questionSet" typeof="ktp:QuestionSet" class="ktp-question-set">
<li class="ktp-question-set-meta">
<section property="ktp:metadata" class="ktp-meta">
<xsl:apply-templates
select="descendant::xhtml:span[#property = 'atom:content-item-name']"/>
</section>
<section property="ktp:tags" class="ktp-meta">
<span class="ktp-meta" property="ktp:questionSetType">shared-stimulus</span>
</section>
</li>
<li class="ktp-stimulus" typeof="ktp:Stimulus" property="ktp:stimulus">
<xsl:apply-templates
select="descendant::xhtml:section[#property = 'ktp:stimulus']"/>
<p class="place-top atom-exclude">Stimulus End: Place content above this line</p>
</li>
<li class="ktp-question" typeof="ktp:Question" property="ktp:question">
<section class="ktp-question-meta">
<section property="ktp:metadata" class="ktp-meta">
<xsl:apply-templates
select="descendant::xhtml:span[#property = 'atom:content-item-name']"/>
</section>
<xsl:apply-templates
select="descendant::xhtml:section[#property = 'ktp:tags']"/>
</section>
<xsl:apply-templates
select="descendant::xhtml:section[#class = 'ktp-question-stem']"/>
<xsl:apply-templates
select="descendant::xhtml:ol[#class = 'ktp-answer-set']"/>
<xsl:apply-templates
select="descendant::xhtml:section[#property = 'ktp:explanation']"/>
</li>
</ol>
</xsl:template>
</xsl:stylesheet>
This is the input section where I need to pull the data-value from:
<section class="ktp-question-meta">
<section property="ktp:metadata" class="ktp-meta"><span property="atom:content-item-name" class="ktp-meta" data-value="lsac820402"></span></section>
<section property="ktp:tags" class="ktp-meta">
<span property="ktp:interactionType" class="ktp-meta">single-select</span>
<span property="ktp:questionType" class="ktp-meta">Assumption (Sufficient)</span>
<span property="ktp:difficulty" class="ktp-meta">★</span>
</section>
This is how I need that section to appear with the .01 added to that data-value:
<section class="ktp-question-meta">
<section property="ktp:metadata" class="ktp-meta">
<span property="atom:content-item-name" class="ktp-meta" data-value="lsac820402.01"></span>
</section>

XSLT multi-level list depending on category name

I want to build a multi level nested list, depending on the category name. This is my xml:
<UserDefinedTable xmlns="DotNetNuke/UserDefinedTable">
<Data>
<UserDefinedRowId>28</UserDefinedRowId>
<Category>KatOne</Category>
<Title>Level 1</Title>
</Data>
<Data>
<UserDefinedRowId>29</UserDefinedRowId>
<Category>KatOneSub</Category>
<Title>Level 2</Title>
</Data>
<Data>
<UserDefinedRowId>30</UserDefinedRowId>
<Category>KatOneSub</Category>
<Title>Level 2</Title>
</Data>
<Data>
<UserDefinedRowId>31</UserDefinedRowId>
<Category>KatTwo</Category>
<Title>Level 1</Title>
</Data>
<Data>
<UserDefinedRowId>32</UserDefinedRowId>
<Category>KatTwoSub</Category>
<Title>Level 2</Title>
</Data>
<Data>
<UserDefinedRowId>33</UserDefinedRowId>
<Category>KatTwoSub</Category>
<Title>Level 2</Title>
</Data>
</UserDefinedTable>
And this is my attempt at the XSLT template:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:udt="DotNetNuke/UserDefinedTable" exclude-result-prefixes="udt">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:variable name="prefix_param">udt_<xsl:value-of select="//udt:Context/udt:ModuleId" />_param</xsl:variable>
<xsl:template match="udt:Data" mode="list">
<xsl:for-each select="udt:Data">
<li>
<span>KatUpper</span>
<ul class="level-two">
<xsl:for-each select="udt:Data">
<li>
KatSub
</li>
</xsl:for-each>
</ul>
</li>
</xsl:for-each>
</xsl:template>
<xsl:template match="/udt:UserDefinedTable">
<xsl:variable name="currentData" select="udt:Data" />
<xsl:if test="$currentData">
<ul class="my-list">
<xsl:apply-templates select="$currentData" mode="list">
</xsl:apply-templates>
</ul>
</xsl:if>
</xsl:template>
<xsl:template name="EditLink">
<xsl:if test="udt:EditLink">
<a href="{udt:EditLink}">
<img border="0" alt="edit" src="{//udt:Context/udt:ApplicationPath}/images/edit.gif" />
</a>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
I would like to make a foreach that check if the cateogry name mathces this or that then it stays on upper level, and inside each iteration i would check for category name to have a nested list inside:
Something like this:
<ul class="my-list">
<li>
<span>KatOne</span>
<ul clasS="levelTwo">
<li>
KatOneSub
</li>
<li>
KatOneSub
</li>
<li>
KatOneSub
</li>
</ul>
</li>
<li>
<span>KatTwo</span>
<ul clasS="levelTwo">
<li>
KatTwoSub
</li>
<li>
KatTwoSub
</li>
</ul>
</li>
</ul>
Your current problem is that within the template matching udt:Data, you do <xsl:for-each select="udt:Data">, but this will be looking for child elements of the current Data element that also called Data. You should really be looking for siblings here.
In XSLT 1.0, you could make use of a key to look up the "Level 2" items based on the first preceding "Level 1" items
<xsl:key name="level2"
match="udt:Data[udt:Title='Level 2']"
use="preceding-sibling::udt:Data[udt:Title='Level 1'][1]/udt:UserDefinedRowId" />
You would then start off by selecting the "Level 1" items
<xsl:variable name="currentData" select="udt:Data[udt:Title='Level 1']" />
<xsl:apply-templates select="$currentData" mode="list" />
Then, to get the "Level 2" items for the current "Level 1" item, you can do use the key
<xsl:for-each select="key('level2', udt:UserDefinedRowId)">
Try this XSLT
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:udt="DotNetNuke/UserDefinedTable"
exclude-result-prefixes="udt">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:key name="level2"
match="udt:Data[udt:Title='Level 2']"
use="preceding-sibling::udt:Data[udt:Title='Level 1'][1]/udt:UserDefinedRowId" />
<xsl:template match="udt:Data" mode="list">
<li>
<span>
<xsl:value-of select="udt:Category" />
</span>
<xsl:if test="key('level2', udt:UserDefinedRowId)">
<ul class="levelTwo">
<xsl:for-each select="key('level2', udt:UserDefinedRowId)">
<li>
<span>
<xsl:value-of select="udt:Category" />
</span>
</li>
</xsl:for-each>
</ul>
</xsl:if>
</li>
</xsl:template>
<xsl:template match="/udt:UserDefinedTable">
<xsl:variable name="currentData" select="udt:Data[udt:Title='Level 1']" />
<xsl:if test="$currentData">
<ul class="my-list">
<xsl:apply-templates select="$currentData" mode="list" />
</ul>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Note, you should change expressions like Title="Level 1" accordingly if you have a better way of identifying Level 1 and Level 2 elements.

How to move an element by attribute value to the last sibling position with xsl?

I need to move (not copy) an element with a given value in attribute id to the last position of its siblings, e.g. //ul/li[#id='b']:
Input:
<ul>
<li id="a">a</li>
<li id="b">b</li>
<li id="c">c</li>
<li id="d">d</li>
...
</ul>
Output:
<ul>
<li id="a">a</li>
<li id="c">c</li>
<li id="d">d</li>
...
<li id="b">b</li>
</ul>
If your input XML is as simple as that in the question, you can use this:
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="ul">
<ul>
<xsl:apply-templates select="li[#id != 'b']"/>
<xsl:apply-templates select="li[#id = 'b']"/>
</ul>
</xsl:template>
<xsl:template match="li">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:transform>
This may help:
<?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" indent="yes" encoding="UTF-8" standalone="yes"/>
<xsl:param name="id" select="string('b')"/>
<xsl:template match="ul">
<xsl:element name="ul">
<xsl:apply-templates select="li[#id!=$id]"/>
<xsl:apply-templates select="li[#id=$id]"/>
</xsl:element>
</xsl:template>
<xsl:template match="li">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>

Need XSLT Transformation to get Un-ordered List of HTML like structure

I am new to XSLT.
I need to transform the below input xml format to the desired output format which is under it (O/P Format is an unorderedList in HTML) using XSLT to use this in a JQuery plugin. I have tried with the below XSLT code myself but i need to add more to it. I am finding hard time to get this transformation done, can any one please help me on this.
Input Format
<Unit id = "2000001">
<Unit id = "2000002">
<Unit id = "2000006">
<Unit id = "2000032">
<Data>
<PartyId>2000032</PartyId>
<PartyTypeCode>DEPT</PartyTypeCode>
<PartyName>2017964 SM Retirement Party</PartyName>
</Data>
</Unit>
<Unit id = "2000033">
<Data>
<PartyId>2000033</PartyId>
<PartyTypeCode>DEPT</PartyTypeCode>
<PartyName>2018370 2012 Director's Ornament</PartyName>
</Data>
</Unit>
<Data>
<PartyId>2000006</PartyId>
<PartyTypeCode>DEPT</PartyTypeCode>
<PartyName>Projects Executive</PartyName>
</Data>
</Unit>
<Data>
<PartyId>2000002</PartyId>
<PartyTypeCode>SEG</PartyTypeCode>
<PartyName>Tres Aguilas Management</PartyName>
</Data>
</Unit>
<Data>
<PartyId>2000001</PartyId>
<PartyTypeCode>SEG</PartyTypeCode>
<PartyName>Tres Aguilas Enterprise</PartyName>
</Data>
</Unit>
Output Format:
<ul>
<li id = "2000001">
<span>Tres Aguilas Enterprise</span>
<ul>
<li id = "2000002">
<span>Tres Aguilas Management</span>
<ul>
<li id = "2000006">
<span>Projects Executive</span>
<ul>
<li id = "2000032">
<span>2017964 SM Retirement Party</span>
</li>
<li id = "2000033">
<span>2018370 2012 Director's Ornament</span>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
XSLT Code:
<?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="/">
<xsl:for-each select="//Unit">
<ul>
<li><xsl:value-of select="Data/PartyName"/></li>
</ul>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This is a "push style" stylesheet that achieves what you want.
<?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"/>
<!--identity template-->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!--convert every <Unit> into a <UL>,
then "push" the attributes(i.e. #id),
and then "push" any <Unit> children-->
<xsl:template match="Unit">
<ul>
<xsl:apply-templates select="#*"/>
</ul>
</xsl:template>
<!--Create an <li> and copy the #id attribute,
then "push" the Data/PartyName that are children of this <Unit>-->
<xsl:template match="Unit/#id">
<li>
<xsl:copy/>
<xsl:apply-templates select="../Data/PartyName"/>
<xsl:apply-templates select="../Unit"/>
</li>
</xsl:template>
<!--convert <PartyName> into <span> -->
<xsl:template match="Data/PartyName">
<span><xsl:value-of select="."/></span>
</xsl:template>
</xsl:stylesheet>
:) Thanks a lot Mads Hansen, for contributing to my question. I finally did changes to the XSLT you gave and succeeded in achieving the Transformation to required Format.
Here is the final 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"/>
<!--identity template-->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!--convert every <Unit> into a <UL>,
then "push" the attributes(i.e. #id),
and then "push" any <Unit> children-->
<xsl:template match="Unit">
<xsl:apply-templates select="#*"/>
</xsl:template>
<!--Create an <li> and copy the #id attribute,
then "push" the Data/PartyName that are children of this <Unit>-->
<xsl:template match="Unit/#id">
<li>
<xsl:copy/>
<xsl:apply-templates select="../Data/PartyName"/>
<xsl:if test= "../Unit">
<ul>
<xsl:apply-templates select="../Unit"/>
</ul>
</xsl:if>
</li>
</xsl:template>
<!--convert <PartyName> into <span> -->
<xsl:template match="Data/PartyName">
<span>
<xsl:value-of select="."/>
</span>
</xsl:template>
</xsl:stylesheet>

XSLT - Dealing with multiple child-nodes

I've got to write and XSLT to deal with the following XML:
<Attachments>
<string>http://lurl/site/Lists/Note/Attachments/image1.jpg</string>
<string>http://lurl/site/Lists/Note/Attachments/image3.jpg</string>
</Attachments>
I need to output the 2 strings, although for some records there are more then 2 strings to output.
e.g.
<ul>
<li>http://lurl/site/Lists/Note/Attachments/image1.jpg</li>
<li>http://lurl/site/Lists/Note/Attachments/image3.jpg</li>
</ul>
Do i need a for each or a while?
A simple apply-templates should do it.
<xsl:template match="Attachments">
<ul>
<xsl:apply-templates/>
</ul>
</xsl:template>
<xsl:template match="string">
<li><xsl:value-of select="."/></li>
</xsl:template>
You don't need any kind of iteration. Use the Identity transformation and override:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Attachments">
<ul>
<xsl:apply-templates select="node()|#*"/>
</ul>
</xsl:template>
<xsl:template match="string">
<li><xsl:value-of select="."/></li>
</xsl:template>
</xsl:stylesheet>
One approach would be to use an xsl:template
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:template match="/">
<ul>
<xsl:apply-templates />
</ul>
</xsl:template>
<xsl:template match="/Attachments/string">
<li>
<xsl:value-of select="." />
</li>
</xsl:template>
</xsl:stylesheet>
<ul>
<xsl:for-each select="//Attachments/string">
<li>
<xsl:value-of select="text()" />
</li>
</xsl:for-each>
</ul>