XSL quoting hell? - xslt

I have a simple XML file that looks like this:
<bases xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<marker>
<name>Sample 1</name>
<adr>Boston</adr>
<state>Mass</state>
<wiki>Link_1</wiki>
</marker>
<marker>
<name>Sample 2</name>
<adr>Essex</adr>
<state>Vermont</state>
</marker>
If there is a <wiki> element, I want to create an HTML link (an <a href..> tag) in the output. If there is no <wiki> element, then output only the name. Here's my XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<table><xsl:text>
</xsl:text>
<tr>
<th>Name</th>
<th>Address</th>
</tr>
<xsl:for-each select="bases/marker">
<xsl:sort select="name" />
<tr>
<td>
<xsl:if test="wiki != ''">
<a href="https://en.wikipedia.com/wiki/<xsl:value-of select='wiki'/>">
</xsl:if>
<xsl:value-of select="name"/>
<xsl:if test="wiki != ''">
</a>
</xsl:if>
</td>
</tr>
<td><xsl:value-of select="adr"/>, <xsl:value-of select="state"/></td>
<xsl:text>
</xsl:text>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
I get a bunch of errors, starting with this one:
Unescaped '<' not allowed in attributes values
<a href="https://en.wikipedia.com/wiki/<xsl:value-of select='wiki'/>">
Help!

I am guessing you want to do:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/bases">
<table>
<tr>
<th>Name</th>
<th>Address</th>
</tr>
<xsl:for-each select="marker">
<xsl:sort select="name" />
<tr>
<td>
<xsl:if test="wiki">
<a href="https://en.wikipedia.com/wiki/{wiki}">
<xsl:value-of select="name"/>
</a>
</xsl:if>
</td>
<td>
<xsl:value-of select="adr"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="state"/>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
To understand this, read about Attribute Value Templates.

Use an attribute value template:
<a href="https://en.wikipedia.com/wiki/{wiki}"/>
Otherwise, you could construct the value of the attribute like this:
<a>
<xsl:attribute name="href">https://en.wikipedia.com/wiki/<xsl:value-of select='wiki'/></xsl:attribute>
</a>
or like this:
<a>
<xsl:attribute name="href">
<xsl:value-of select="concat('https://en.wikipedia.com/wiki/', wiki)"/>
</xsl:attribute>
</a>

Related

How to build a string with XSLT?

I have this XML snippet
<MediaTypes>
<DVD>true</DVD>
<HDDVD>false</HDDVD>
<BluRay>true</BluRay>
</MediaTypes>
And I want to create an output that looks like this
DVD / Blu-ray
or like this
DVD
or like this
DVD / HD-DVD / Blu-ray
depending on the true/false states
But I'm a total beginner in the XSLT game. :-/
Here's a snippet of the full XML
<?xml version="1.0" encoding="Windows-1252"?>
<Collection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<DVD>
<ProfileTimestamp>2012-06-07T05:20:51Z</ProfileTimestamp>
<ID>0044005507027.5</ID>
<MediaTypes>
<DVD>true</DVD>
<HDDVD>false</HDDVD>
<BluRay>false</BluRay>
</MediaTypes>
<UPC>0-044005-507027</UPC>
<CollectionNumber>448</CollectionNumber>
<CollectionType IsPartOfOwnedCollection="true">Owned</CollectionType>
<Title>The Big Lebowski</Title>
<DistTrait />
<OriginalTitle />
<CountryOfOrigin>United States</CountryOfOrigin>
...
</DVD>
<DVD>
...
</DVD>
</Collection>
I tried to follow the advices given below (regarding the loop), but it doesn't seem to work.
This is the XSL I have so far (complete):
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template name="MediaTypes" match="MediaTypes">
Test
<xsl:for-each select="*[.='true']">
<xsl:value-of select="name()"/>
<xsl:if test="position()!=last()">
<xsl:text>/</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template match="/">
<html>
<head>
<title>DVD Profiler Double Dips</title>
</head>
<body>
<div>
<h1>DVD Profiler Double Dips</h1>
</div>
<table border="1" cellpadding="3" cellspacing="0">
<tr>
<th>Title</th>
<th>Edition</th>
<th>Production Year</th>
<th>DVD /</th>
<th>Purchase Date</th>
<th>Collection Type</th>
<th>Original Title</th>
<th>Sort Title</th>
</tr>
<xsl:for-each select="/Collection/DVD">
<tr>
<td>
<xsl:value-of select="Title"/>
</td>
<td>
<xsl:if test="DistTrait != ''">
<xsl:value-of select="DistTrait"/>
</xsl:if>
<xsl:if test="DistTrait = ''">
 
</xsl:if>
</td>
<td>
<xsl:if test="ProductionYear != ''">
<xsl:value-of select="ProductionYear"/>
</xsl:if>
<xsl:if test="ProductionYear = ''">
 
</xsl:if>
</td>
<td>
<xsl:call-template name="MediaTypes" />
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
But where
<xsl:call-template name="MediaTypes" />
is standing, only "Test" comes out
Yay!
You guys are awesome. Yes
<xsl:apply-templates select="MediaTypes"/>
Was my weapon of choice.
My final template looks like this:
<xsl:template name="MediaTypes" match="MediaTypes">
<xsl:for-each select="*[.='true']">
<xsl:value-of select="name()"/>
<xsl:if test="position()!=last()">
<xsl:text> / </xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:if test="CustomMediaType != ''">
<xsl:if test="*[.='true']">
<xsl:text> / </xsl:text>
</xsl:if>
<xsl:value-of select="CustomMediaType"/>
</xsl:if>
</xsl:template>
begins to understand how stuff works
Thanks for all your help!
The question is rather trivial - the real problem is that you only show a snippet of the XML document. XSLT is very much context-dependent.
Here is a template to match any <MediaTypes> element and output the required string; hopefully you'll know how to incorporate it into your own stylesheet:
<xsl:template match="MediaTypes">
<xsl:for-each select="*[.='true']">
<xsl:value-of select="name()"/>
<xsl:if test="position()!=last()">
<xsl:text>/</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:template>
I'd advice you to change the xml a bit
Here is an example
<?xml version="1.0" ?>
<?xml-stylesheet type="text/xsl" ?>
<MediaTypes>
<Media name="DVD">true</Media>
<Media name="HDDVD">false</Media>
<Media name="BluRay">true</Media>
</MediaTypes>
This way you may write XPATH expressions like //MediaTypes/Media
However did #michael.hor257k write a nice solution, jacking this xml pattern into that would look like this
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="//MediaTypes">
<xsl:for-each select="*[.='true']">
<xsl:value-of select="#name"/>
<xsl:if test="position()!=last()">
<xsl:text>/</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This will render DVD/BluRay
Test here http://xslttest.appspot.com/

how to design a template in xslt which adds strong tag based on some conditions

I have input xml of the form
<content xml:lang="en" xmlns:w="http://www.w.com/sch/W">
<w:firstRowHeader>true</w:firstRowHeader>
<w:firstColumnHeader>true</w:firstColumnHeader>
<w:customTable>
<w:tableContent>
<w:row>
<w:cell>
<w:spanInfo backgroundColor="Yellow" columnWidth="5" isRowHeader="true"/>
<text>ghnmghmg</text>
</w:cell>
<w:cell>
<w:spanInfo backgroundColor="Yellow" isRowHeader="false"/>
<text>ghmhgmgm</text>
</w:cell>
</w:row>
<w:row>
<w:cell>
<w:spanInfo backgroundColor="Yellow" columnWidth="5" isRowHeader="false"/>
<text>vj</text>
</w:cell>
<w:cell>
<w:spanInfo columnWidth="5" isRowHeader="true"/>
<text>mm</text>
</w:cell>
</w:row>
</w:tableContent>
</w:customTable>
</content>
This needs to be transformed to a xml in which:
w:tableContent mapped to tablecontent and
then under tablecontent tag 'table','tbody' tags are created
w:row mapped to tr tag
w:cell mapped to td tag
and conditions are like
if only 1st w:cell element in w:row has an attribute isRowHeader as "true" then every 'td' element under its respective 'tr' tag should contain a 'strong' tag and ignore 2nd w:cell's isRowHeader
if w:firstRowHeader is 'true' then transformed table should have 1st row text in bold, i.e., every 'td' tag in 1st row of table should contain 'strong' tag
if w:firstColumnHeader is 'true' then transformed table should have 1st column text in bold, i.e., every tr tag's 1st 'td' tag of table should contain 'strong' tag
Transformed xml:
<content>
<tablecontent>
<table cellspacing="1" cellpadding="1" border="1" style="WIDTH: 100%" title="Title" xmlns="http://www.w3.org/1999/xhtml">
<tbody>
<tr>
<td style="BACKGROUND-COLOR: Yellow; WIDTH: 5%"><strong>ghnmghmg</strong></td>
<td style="BACKGROUND-COLOR: Yellow"><strong>ghmhgmgm</strong></td>
</tr>
<tr>
<td style="BACKGROUND-COLOR: Yellow; WIDTH: 5%">vj</td>
<td style="WIDTH: 5%">mm</td>
</tr>
</tbody>
</table>
</tablecontent>
</content>
This is the xslt template that i have tried but cant figure out how to implement these 'strong' tags in it...
XSLT:
<xsl:template match="w:tableContent">
<xsl:variable name="var3" select="../w:firstRowHeader"/>
<xsl:variable name="var4" select="../w:firstColumnHeader"/>
<tablecontent>
<table cellspacing="1" cellpadding="1" border="1" style="WIDTH: 100%" title="Title" xmlns="http://www.w3.org/1999/xhtml" >
<tbody>
<xsl:for-each select="child::*">
<xsl:choose>
<xsl:when test="name()='w:row'">
<tr>
<xsl:for-each select="child::*">
<xsl:choose>
<xsl:when test="name()='w:cell'">
<td>
<xsl:for-each select="child::*">
<xsl:choose>
<xsl:when test="name()='w:spanInfo'">
<xsl:variable name="var8" select="#backgroundColor" />
<xsl:variable name="var9" select="#columnWidth" />
<xsl:variable name="var10" select="#isRowHeader" />
<xsl:if test="$var8!='' or $var9!=''">
<xsl:attribute name="style">
<xsl:if test="$var8!='' and $var9!=''">
<xsl:value-of select="concat('BACKGROUND-COLOR: ',$var8,'; WIDTH: ',$var9,'%')" />
</xsl:if>
<xsl:if test="$var8!='' and not($var9)">
<xsl:value-of select="concat('BACKGROUND-COLOR: ',$var8)" />
</xsl:if>
<xsl:if test="not($var8) and $var9!=''">
<xsl:value-of select="concat('WIDTH: ',$var9,'%')" />
</xsl:if>
</xsl:when>
<xsl:when test="name()='text'">
<xsl:if test="../w:spanInfo/#isRowHeader='true'">
<strong><xsl:value-of select="." /></strong>
</xsl:if>
<xsl:if test="../w:spanInfo/#isRowHeader!='true' or not(../w:spanInfo/#isRowHeader) ">
<xsl:value-of select="." />
</xsl:if>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</td>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</tr>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</tbody>
</table>
</tablecontent>
</xsl:template>
But the above template adds 'strong' tags to the cells which has only w:spanInfo's 'isRowHeader' attribute as 'true'. But I require to get 'strong' tag to be added to 2nd cell content also irrespective of its w:spanInfo's 'isRowHeader' attribute's value, provided if 1st cell already has 'isRowHeader' attribute as 'true'.
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:w="http://www.w.com/sch/W">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<content>
<xsl:apply-templates select="*/*/w:tableContent"/>
</content>
</xsl:template>
<xsl:template match="w:tableContent">
<table cellspacing="1" cellpadding="1" border="1" style="WIDTH: 100%" title="Title" xmlns="http://www.w3.org/1999/xhtml">
<tbody>
<xsl:apply-templates select="w:row" />
</tbody>
</table>
</xsl:template>
<xsl:template match="w:row">
<tr>
<xsl:apply-templates select="w:cell" />
</tr>
</xsl:template>
<xsl:template match="w:cell">
<xsl:variable name="style">
<xsl:if test="w:spanInfo/#backgroundColor">
<xsl:value-of select="concat('BACKGROUND-COLOR: ',w:spanInfo/#backgroundColor)" />
</xsl:if>
<xsl:if test="w:spanInfo/#columnWidth">
<xsl:if test="w:spanInfo/#backgroundColor">
<xsl:value-of select="'; '" />
</xsl:if>
<xsl:value-of select="concat('WIDTH: ',w:spanInfo/#columnWidth,'%')" />
</xsl:if>
</xsl:variable>
<td>
<xsl:if test="$style">
<xsl:attribute name="style"><xsl:value-of select="$style" /></xsl:attribute>
</xsl:if>
<xsl:apply-templates select="text" />
</td>
</xsl:template>
<xsl:template match="w:cell/text[
not( ../../preceding-sibling::w:row) and (/*/w:firstRowHeader='true')
or
not( ../preceding-sibling::w:cell) and (/*/w:firstColumnHeader='true')
or
(../preceding-sibling::w:cell[last()]/w:spaninfo/#isRowHeader='true')
]">
<strong><xsl:call-template name="default-rendering-of-text" /></strong>
</xsl:template>
<xsl:template match="text" name="default-rendering-of-text">
<xsl:value-of select="." />
</xsl:template>
</xsl:stylesheet>
... should satisfy your rules. The 3 conditions that you have set for bold/strong rendering are made plainly evident by the predicate of the match condition for text elements (near the end of the style-sheet). By avoiding unnecessary xsl:for-each we can use a simpler, more modular and more readable template based solution.

How do you link two elements together when they have the same name in xslt?

I have these trees, one with this structure /cars/car and the second /maker/cars/car. The first one has a reference to the id of the second list of cars.
<xsl:template match="t:cars/t:car">
<tr>
<td>
<xsl:if test="position()=1">
<b><xsl:value-of select="../#name"/><xsl:text> </xsl:text></b>
</xsl:if>
</td>
</tr>
I have this, it was filled in with a for loop I learn after a bit that I could't do it.
This is what it was before:
<xsl:template match="t:cars/t:car">
<tr>
<td>
<xsl:if test="position()=1">
<b><xsl:value-of select="../#name"/><xsl:text> </xsl:text></b>
</xsl:if>
<xsl:for-each select="/t:root/t:maker/t:car">
<xsl:if test="t:root/t:maker/#id = #ref">
<xsl:value-of select="#title"/>
</xsl:if>
</xsl:for-each>
</td>
</tr>
sample:
auto>
<maker type="toyota">
<car name="prius" id="1"/>
</maker>
<cars name="My Collection">
<car ref="1" />
</cars>
This simple 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:key name="kCarById" match="maker/car" use="#id"/>
<xsl:template match="/*">
<table>
<xsl:apply-templates/>
</table>
</xsl:template>
<xsl:template match="cars/car">
<tr>
<td>
<b>
<xsl:value-of select="key('kCarById', #ref)/#name"/>
</b>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document (the provided one, just extended a little):
<auto>
<maker type="toyota">
<car name="prius" id="1"/>
</maker>
<maker type="honda">
<car name="accord" id="2"/>
</maker>
<maker type="benz">
<car name="mercedes" id="3"/>
</maker>
<cars name="My Collection">
<car ref="2" />
<car ref="3" />
</cars>
</auto>
produces the wanted, correct result:
<table>
<tr>
<td>
<b>accord</b>
</td>
</tr>
<tr>
<td>
<b>mercedes</b>
</td>
</tr>
</table>
Explanation: Appropriate use of keys.

XSLT - Select only one set of data

Need some help: I want to only pull back the results for a specific ItemId, so if i wanted the only data for ItemId how could I adapt the template below?
I've tried for about 2 hours without any luck...
Here is a sample of my XML:
This Xslt only brings the item with ItemId value equal to 2075.
Notice I added [ItemInfo/PeriodAttendanceInfo/ItemId=2075].
<?xml version="1.0" encoding="utf-8"?>
<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:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:call-template name="dvt_2"/>
</xsl:template>
<xsl:template name="dvt_2">
<xsl:variable name="Rows" select="/ArrayOfPeriodAttendanceGroup/PeriodAttendanceGroup[ItemInfo/PeriodAttendanceInfo/ItemId=2075]"/>
<xsl:variable name="dvt_RowCount" select="count($Rows)"/>
<xsl:call-template name="dvt_2.rowContents">
<xsl:with-param name="Rows" select="$Rows"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="dvt_2.rowContents">
<xsl:param name="Rows"/>
<xsl:for-each select="$Rows">
<xsl:sort select="ItemInfo/PeriodAttendanceInfo" order="ascending"/>
<xsl:call-template name="dvt_2.rowContentsView"/>
</xsl:for-each>
</xsl:template>
<xsl:template name="dvt_2.rowContentsView">
<xsl:variable name="subjectName" select="ItemInfo/PeriodAttendanceInfo/ItemText"/>
<tr class="{$subjectName}">
<xsl:element name="td">
<xsl:attribute name="class">
<xsl:value-of select="ItemInfo/PeriodAttendanceInfo/ItemText"/>
</xsl:attribute>
<xsl:variable name="subjectID" select="ItemInfo/PeriodAttendanceInfo/ItemId"/>
<a class="leftAlign" href="../Departments/{$subjectID}">
<xsl:value-of select="ItemInfo/PeriodAttendanceInfo/ItemText"/>
</a>
</xsl:element>
<td>
<p class="name">
<xsl:value-of select="Total"/>
</p>
</td>
<td>
<p class="name">
<xsl:value-of select="Present"/>
</p>
</td>
<td>
<p class="name">
<xsl:value-of select="Late"/>
</p>
</td>
<td>
<p class="name">
<xsl:value-of select="UnauthorisedAbsence"/>
</p>
</td>
</tr>
</xsl:template>
<xsl:template name="dvt_1.empty">
<xsl:variable name="dvt_ViewEmptyText">There is no timetable view for today</xsl:variable>
<table border="0" width="100%">
<tr>
<td class="ms-vb">
<xsl:value-of select="$dvt_ViewEmptyText"/>
</td>
</tr>
</table>
</xsl:template>
</xsl:stylesheet>

xsl : getting specific node values from a table

My XML Code
<DBE:Attribute name="Test1" type="Table">
<DBE:Table>
<DBE:TableHeader>
<DBE:TableColumn>t1</DBE:TableColumn>
<DBE:TableColumn>t2</DBE:TableColumn>
<DBE:TableColumn>t3</DBE:TableColumn>
<DBE:TableColumn>t4</DBE:TableColumn>
<DBE:TableColumn>t5</DBE:TableColumn>
<DBE:TableColumn>t6</DBE:TableColumn>
<DBE:TableColumn>t7</DBE:TableColumn>
<DBE:TableColumn>t8</DBE:TableColumn>
<DBE:TableColumn>t9</DBE:TableColumn>
<DBE:TableColumn>t10</DBE:TableColumn>
<DBE:TableColumn>t11</DBE:TableColumn>
<DBE:TableColumn>t12</DBE:TableColumn>
<DBE:TableColumn>t13</DBE:TableColumn>
</DBE:TableHeader>
<DBE:TableRow>
<DBE:TableData>0300 </DBE:TableData>
<DBE:TableData/>
<DBE:TableData>25</DBE:TableData>
<DBE:TableData>25</DBE:TableData>
<DBE:TableData>2009/09/03</DBE:TableData>
<DBE:TableData/>
<DBE:TableData>BAG</DBE:TableData>
<DBE:TableData>rrr</DBE:TableData>
<DBE:TableData>Yes</DBE:TableData>
<DBE:TableData>12</DBE:TableData>
<DBE:TableData>2009/03/09</DBE:TableData>
<DBE:TableData>GO</DBE:TableData>
<DBE:TableData/>
</DBE:TableRow>
</DBE:Table>
</DBE:Attribute>
I would like my output to be ->
t7 t5 t1 t13 --> Header
---------------------------------------------------------------
BAG 2009/09/03 0300 GO --> ROW1
.............................................................. --> ROW2
and so on
My XSL code --> (for only selected values to be displayed)
<xsl:for-each select="DBE:Attribute[#name='Test1']/DBE:Table/DBE:TableRow">
<tr bgcolor="white">
<xsl:for-each select="DBE:TableData">
<td>
<xsl:value-of select="node()|*">
</xsl:value-of>
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
You could create a selector variable that holds the numeric position of columns you want. See the variable named "selector" below. Once you have that, a simple if in your TableData and TableColumn templates does the rest.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:DBE="foo.bar">
<xsl:variable name="selector">;1;5;7;12;</xsl:variable>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="DBE:Attribute">
<html>
<body><xsl:apply-templates/></body>
</html>
</xsl:template>
<xsl:template match="DBE:Table">
<table border="1">
<xsl:apply-templates/>
</table>
</xsl:template>
<xsl:template match="DBE:TableHeader">
<tr>
<xsl:apply-templates select="DBE:TableColumn"/>
</tr>
</xsl:template>
<xsl:template match="DBE:TableColumn">
<xsl:variable name="pos">
<xsl:text>;</xsl:text><xsl:value-of select="string(position())"/><xsl:text>;</xsl:text>
</xsl:variable>
<xsl:if test="contains($selector, $pos)">
<th>
<xsl:value-of select="."/>
</th>
</xsl:if>
</xsl:template>
<xsl:template match="DBE:TableRow">
<tr>
<xsl:apply-templates select="DBE:TableData"/>
</tr>
</xsl:template>
<xsl:template match="DBE:TableData">
<xsl:variable name="pos">
<xsl:text>;</xsl:text><xsl:value-of select="string(position())"/><xsl:text>;</xsl:text>
</xsl:variable>
<xsl:if test="contains($selector, $pos)">
<td>
<xsl:value-of select="."/>
</td>
</xsl:if>
</xsl:template>
</xsl:stylesheet>