Processing Dynamically Created Children and Siblings' Children - html

I've been editing and looking at this for so long I'm failing to find a seemingly easy solution.
An element (and its sibling) will have any number of different (dynamic) results, which I would like to display in an HTML table, one row for each cd-file pair. Initially thinking I could avoid for-each loops (which seem to return the same results anyway), I need a [better] way to refer to the children. The source below is similar in structure with which I'm working.
For example, some "cd"s and "file"s will have more elements such as price, some might have no information.
XML
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="cdcatalog.test2.xsl"?>
<catalog>
<rock>
<album>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
</cd>
<file>
<store>Apple</store>
<size>3823</size>
</file>
</album>
<album>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
</cd>
<file>
<store>Amazon</store>
<size>2123</size>
</file>
</album>
</rock>
</catalog>
XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<tr bgcolor="#cccccc">
<th style="text-align:left">Title</th>
<th style="text-align:left">Artist</th>
<th style="text-align:left">Store</th>
<th style="text-align:left">Size</th>
</tr>
<xsl:apply-templates/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="album">
<tr>
<xsl:apply-templates select="cd"/>
<xsl:apply-templates select="file"/>
</tr>
</xsl:template>
<xsl:template match="cd">
<td>
<xsl:value-of select="."/>
</td>
</xsl:template>
<xsl:template match="file">
<td>
<xsl:value-of select="."/>
</td>
</xsl:template>
</xsl:stylesheet>
Results
<table border="1">
<tbody>
<tr bgcolor="#cccccc">
<th style="text-align:left">Title</th>
<th style="text-align:left">Artist</th>
<th style="text-align:left">Store</th>
<th style="text-align:left">Size</th>
</tr>
<tr>
<td>
Empire Burlesque
Bob Dylan
</td>
<td>
Apple
3823
</td>
</tr>
<tr>
<td>
Hide your heart
Bonnie Tyler
</td>
<td>
Amazon
2123
</td>
</tr>
</tbody>
</table>
Result View
Now, I think I see what is happening. From my interpretation, in the XSLT file, under the "cd" and "file" templates, select="." is returning all the children inside the <td>. I need a way for each to have a <td>. I've tried such things as call-template for each "cd" and "file," as well as for-each within the "cd" and "file" templates. Note that I also need to dynamically build the <th> elements, for now they're created manually for testing purposes. Any suggestions?

As an educated guess (it is not exactly clear what your expected output looks like, and what aspects of the input file are variable), I think you want the following.
There is no need to explicitly apply templates to the cd and title elements, just apply-templates in a general fashion. Also, your stylesheet is currently missing templates for store, size etcetera. As far as I understand, those are the element whose content should go inside td elements in the HTML output, so you need to match them with templates, even if those templates are rather general.
If you do not know the names of the table headers th beforehand and if there is a variable number of leaf elements that are grandchildren of album, here is a more "dynamic" solution:
XSLT Stylesheet
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" doctype-public="XSLT-compat" omit-xml-declaration="yes"
encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<tr bgcolor="#cccccc">
<xsl:for-each select="distinct-values(catalog/rock/album/*/*/name())">
<th style="text-align:left">
<xsl:value-of select="."/>
</th>
</xsl:for-each>
</tr>
<xsl:apply-templates/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="album">
<tr>
<xsl:apply-templates/>
</tr>
</xsl:template>
<xsl:template match="album/*/*">
<td>
<xsl:value-of select="."/>
</td>
</xsl:template>
</xsl:stylesheet>
HTML Output
<!DOCTYPE html
PUBLIC "XSLT-compat">
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<tr bgcolor="#cccccc">
<th style="text-align:left">title</th>
<th style="text-align:left">artist</th>
<th style="text-align:left">store</th>
<th style="text-align:left">size</th>
</tr>
<tr>
<td>Empire Burlesque</td>
<td>Bob Dylan</td>
<td>Apple</td>
<td>3823</td>
</tr>
<tr>
<td>Hide your heart</td>
<td>Bonnie Tyler</td>
<td>Amazon</td>
<td>2123</td>
</tr>
</table>
</body>
</html>
Rendered HTML
Try this solution online here.

If I'm guessing correctly, you want to do something like:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:template match="/catalog">
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<tr>
<xsl:apply-templates select="*/album[1]" mode="header"/>
</tr>
<xsl:apply-templates/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="album">
<tr>
<xsl:apply-templates/>
</tr>
</xsl:template>
<xsl:template match="*[text()]" mode="header">
<th>
<xsl:value-of select="name()"/>
</th>
</xsl:template>
<xsl:template match="*[text()]">
<td>
<xsl:value-of select="."/>
</td>
</xsl:template>
</xsl:stylesheet>
This create a row for every album, and a column for every leaf node with a text node.
Note that this assumes that your input is regular - i.e. that every album has the same properties, in the same order, and none of them is empty.

Related

XSLT template to match multiple nodes is repeating the html

I am facing issues while displaying the xml in html table using xsl.
1. The html view is repeating with the below xsl and
2. unable to differentiate sub elements with the same name (Eg: name tag in the below xml).
The xml is having different kinds of information in different nodes as follows.
<employee>
<address>
<street>street1</street>
<city>city1</city>
<pincode>123456</pincode>
</address>
<personalinfo>
<name>testname1</name>
<phone>999999999</phone>
<dob>23-09-34</dob>
</personalinfo>
<remarks>
<education>
<name>testname2</name>
<college>college1</college>
<gpa>7.5</gpa>
</education>
</remarks>
</employee>
and Here is my xsl
<?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="employee">
<html>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="address|personalinfo|remarks">
<table width="630">
<tr>
<td>Name</td>
<td>College</td>
<td>City</td>
</tr>
<tr>
<td><xsl:value-of select="//name"/></td>
<td><xsl:value-of select="//college"/></td>
<td><xsl:value-of select="//city"/></td>
</tr>
</table>
<span><br/>
</span>
</xsl:template>
</xsl:stylesheet>
Please help me in this regard. Thank you.
Judging from your table structure, you want to move the table header up to the template matching the root node, and then get the corresponding detail of each employee using a relative (and explicit) path:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:template match="/">
<html>
<body>
<table>
<tr>
<th>Name</th>
<th>College</th>
<th>City</th>
</tr>
<xsl:apply-templates/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="employee">
<tr>
<td>
<xsl:value-of select="personalinfo/name"/>
</td>
<td>
<xsl:value-of select="remarks/education/college"/>
</td>
<td>
<xsl:value-of select="address/city"/>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
The expression //name selects all the name elements anywhere in the document. That's not you want: you want the name element within the personalInfo or remarks that you are currently processing: that's select="name" for an immediate child, or select=".//name" for a descendant at any depth.
It seems odd to use the same template rule to process three elements (address|personalinfo|remarks) that have very different structure.

xsl template is overriding with xsl include

I am trying to create some reusable templates while developing a html page for xml with xsl. But when I include the other xsl's, the main xsl template is getting overridden instead of adding it to it. please suggest.
Here is my xml,
<employee>
<address>
<street>street1</street>
<city>city1</city>
<doornumber>1-23</doornumber>
<pincode>123456</pincode>
</address>
<personalinfo>
<name>testname1</name>
<phone>999999999</phone>
<dob>23-09-34</dob>
</personalinfo>
<remarks>
<education>
<name>testname2</name>
<college>college1</college>
<gpa>7.5</gpa>
</education>
</remarks>
<data>
<name>data1</name>
</data>
<data>
<name>data2</name>
</data>
<data>
<name>data3</name>
</data>
<data>
<name>data4</name>
</data>
<data>
<name>data5</name>
</data>
</employee>
Here is my main xsl,
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:template match="/">
<html>
<body>
<table>
<tr>
<th>Name</th>
<th>College</th>
<th>City</th>
</tr>
<xsl:apply-templates/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="employee">
<tr>
<td>
<xsl:value-of select="personalinfo/name"/>
</td>
<td>
<xsl:value-of select="remarks/education/college"/>
</td>
<td>
<xsl:value-of select="address/city"/>
</td>
</tr>
</xsl:template>
<!-- <xsl:include href="test_include1.xsl" /> -->
<!-- <xsl:include href="test_include2.xsl" /> -->
</xsl:stylesheet>
Here is my test_include1.xsl,
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:template match="employee">
<table><tr>
<th>data names</th></tr>
<xsl:for-each select="data">
<tr>
<td>
<xsl:value-of select="name"/>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
Here is my test_include2.xsl,
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:template match="employee">
<table><tr>
<th>Name</th>
<th>College</th></tr>
<tr>
<td>
<xsl:value-of select="personalinfo/name"/>
</td>
<td>
<xsl:value-of select="remarks/education/college"/>
</td>
</tr>
</table>
</xsl:template>
</xsl:stylesheet>
I am trying to modularize the same xml data in different templates so that I can reuse the same templates in other stylesheets. Please suggest how I can acheive this. Thank you.
Here is the expected result,
<html><body>
<table>
<tr>
<th>Name</th>
<th>College</th>
<th>City</th>
</tr>
<tr>
<td>testname1</td>
<td>college1</td>
<td>city1</td>
</tr>
</table>
<table>
<tr><th>data names</th></tr>
<tr><td>data1</td></tr>
<tr><td>data2</td></tr>
<tr><td>data3</td></tr>
<tr><td>data4</td></tr>
<tr><td>data5</td></tr>
</table>
<table>
<tr>
<th>Name</th>
<th>College</th>
</tr>
<tr>
<td>testname1</td>
<td>college1</td>
</tr>
</table>
</body></html>
Firstly, I don't see any xsl:include declarations in any of these stylesheets, so I'm not quite sure what you are doing. [Sorry - missed the pale grey commented out code - poor contrast on my monitor.]
Secondly, with XSLT 1.0 the behaviour when you have two templates matching the same node, with the same precedence and priority, is not very well defined. Technically it's an error, but the processor is allowed to recover by choosing the rule that comes "last in the stylesheet" - which itself is not particularly well defined when there are multiple modules involved.
Thirdly, you say "the main xsl template is getting overridden instead of adding it to it". This suggests you have some particular expectation of how you want it to behave, but I can't really work out what this expectation is. You haven't given any expected results, so I don't know what you are trying to achieve. But it will always be the case, if you have more than one rule that matches a node, that you either get an error, or exactly one of the rules is executed - they are never combined in any way.
This could be achieved by modes.
Another way is to give each xsl:template match a priority number. The one which you want to get executed should have the highest priority. Priority is attribute of xsl:template
<xsl:template match = "employee" priority = "4"> do the task </xsl:template>

Showing images from xml link via xslt

Basically I want images to be shown in my table if I have image links in my xml. I know I'm supposed to use attribute but whatever I tried, nothing worked.
XML
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="catalog.xsl"?>
<catalog>
<cd>
<title>Empire Burlesque</title>
<picture url="https://www.planwallpaper.com/static/images/Winter-Tiger-Wild-Cat-Images.jpg" />
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Hide your heart</title>
<picture url="http://i.istockimg.com/cms/resources/images/HomePage/Tiles/US_March2016/SilenceAndNoise_Signature_78262195.jpg" />
<artist>Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
</catalog>
XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Title</th>
<th>Artist</th>
</tr>
<xsl:for-each select="catalog/cd">
<tr>
<td><xsl:value-of select="title"/></td>
<td><xsl:value-of select="artist"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
So I want instead of my links, the actual images to be shown. How would I do that? As far as I got, I get X's instead of the actual images for some reason. Any ideas why?
I tried Tim C's code and it slightly works. In his link it works but when I run it on my pc, it doesn't show the images anymore
I checked it on every browser, both windows and linux and same problem. No images appear using Tim's code. I even uploaded it on a server and the problem persists. The photos look like they are corupted or something. Any ideas?
You use the img tag in HTML, with the src attribute to show an attribute, so if you were in a template that matched picture you would do it like this
<xsl:template match="picture">
<img src="{#url}" />
</xsl:template>
The curly braces are known as "Attribute Value Templates". The expression inside the curly braces is evaluated to produce a value.
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" doctype-public="XSLT-compat" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Title</th>
<th>Artist</th>
<th>Image</th>
</tr>
<xsl:for-each select="catalog/cd">
<tr>
<td><xsl:value-of select="title"/></td>
<td><xsl:value-of select="artist"/></td>
<td>
<xsl:apply-templates select="picture" />
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="picture">
<img src="{#url}" />
</xsl:template>
</xsl:stylesheet>
Note that if the input XML had multiple pictures for a cd, this would should all of them. To show just the first picture in this case, do <xsl:apply-templates select="picture[1]" />
Also note it might be preferred to specify a height and width for your images too, although you would need to amend the XML for that, but you add the attributes to the image in the same was as the url.
<xsl:template match="picture">
<img src="{#url}" height="{#height}px" width="{#width}px" />
</xsl:template>

XML + XSL to XSLT for an HTML

Sorry if my Post won´t be so good i am not that good in expressing myself.
What i want:
I´ve got an XML and a XSL Data i want to convert to an XSLT for an HTML.
As transformating i use Altova XMLSpy and this webpage: http://www.freeformatter.com/xsl-transformer.html
What i´ve got:
My XML:
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="Style.xsl"?>
<Configuration>
<Environment>
...
</Environment>
<Configurations>
...
<Config>
...
</Config>
</Configurations>
</Configuration>
My XSL:
<?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="/">
<html>
<body>
<table border="4">
<xsl:apply-templates select="Configuration"/>
</table>
</body>
</html>>
</xsl:template>
<xsl:template match="Environment">
<tr>Environment</tr>
<br/>
<td> <th>Text</th>
<xsl:value-of select="Environment"/>
</td>
<td> <th>Text2</th>
<xsl:value-of select="config"/>
</td>
...
...
</xsl:template>
</xsl:stylesheet>
This is just a example how i build it.
so far everything was fine, but then:
Problem:
If i start my XML in Firefox now everything works. If i start it in IE or Chrome i just get the XML Data but not the Table.
Trying to transform it in XSLT gives me this Error:
Unable to perform XSL transformation on your XML file. null
I hope you can help me. I am a bloody beginner in XML/XSl/XSLT but i hope my post will give you the Information you need.
Greetings
Max
You have made a very common beginner's mistake:
<xsl:template match="Environment">
<tr>Environment</tr>...
<xsl:value-of select="Environment"/>
In this template, the context node is an Environment element, and your xsl:value-of instruction is looking for a child of that element with the name Environment. But your Environment element (I assume) does not have any Environment children. What you should write (I assume) is
<xsl:value-of select="."/>
However, I don't think this is the whole problem. It doesn't account for all the symptoms described. You've actually made another beginner's mistake, which is trying to run your XSLT code in the browser before testing it elsewhere. The browser-based XSLT engines give lousy diagnostics (even when you use something like Firebug). Get your code working first in a desktop environment - preferably an XML IDE like oXygen. You'll then get a decent explanation of what is wrong.
Hi Here Is The Modified Version of Your Code.
XSL Template Match Must match the XML node you wish to apply templates.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:stylesheet version="2.0">
<xsl:template match="Configuration">
<html>
<body>
<table border="4">
<xsl:apply-templates select="Environment"/>
</table>
<table border="4">
<xsl:apply-templates select="Configurations"/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="Environment">
<tr>
<th>
<xsl:value-of select="name()"/>
</th>
</tr>
<tr>
<td>
<xsl:value-of select="."/>
</td>
</tr>
</xsl:template>
<xsl:template match="Configurations">
<tr>
<th>
<xsl:value-of select="name()"/>
</th>
</tr>
<tr>
<td>
<xsl:apply-templates select="Config"/>
</td>
</tr>
</xsl:template>
<xsl:template match="Config">
<tr>
<th>
<xsl:value-of select="name()"/>
</th>
</tr>
<tr>
<td>
<xsl:value-of select="."/>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>

To transform XML to HTML in table format

I want to change this xml content to HTML table
<SSI>
<data>
<expanded>Chemical Research</expanded><abbre>Chem. Res.</abbre>
<expanded>Materials Journal</expanded><abbre>Mater. J.</abbre>
<expanded>Chemical Biology</expanded><abbre>Chem. Biol.</abbre>
<expanded>Symposium Series</expanded><abbre>Symp. Ser.</abbre>
<expanded>Biochimica Polonica</expanded><abbre>Biochim. Pol.</abbre>
<expanded>Chemica Scandinavica</expanded><abbre>Chem. Scand.</abbre>
<\data>
<data>
<expanded>Botany</expanded><abbre>Bot.</abbre>
<expanded>Chemical Engineering</expanded><abbre>Chem. Eng.</abbre>
<expanded>Chemistry</expanded><abbre>Chem.</abbre>
<expanded>Earth Sciences</expanded><abbre>Earth Sci.</abbre>
<expanded>Microbiology</expanded><abbre>Microbiol.</abbre>
<\data>
<\SSI>
Tried with following XSL
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<html>
<head><title>Abbreviate</title></head>
<body>
<table border="1">
<tr>
<th>Expanded</th>
<th>Abbre</th>
</tr>
<xsl:for-each select="SSI/data">
<tr>
<td><xsl:value-of select="expanded"/></td>
<td><xsl:value-of select="abbre"/></td>
</tr>
</xsl:for-each>
</table>
</body></html>
</xsl:template>
</xsl:stylesheet>
I got only the first entry of data tag in HTML Table format
Expanded Abbre
----------- --------------------
Chemical Research Chem. Res
Botany Bot.
how can get all the values in HTMl???
If you clean up your XSLT and use xsl:apply-templates rather than xsl:for-each, life will become simpler. There is almost never a reason to use xsl:for-each. Try this:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<html>
<head><title>Abbreviate</title></head>
<body>
<table border="1">
<tr>
<th>Expanded</th>
<th>Abbre</th>
</tr>
<xsl:apply-templates select='SSI/data/expanded'/>
</table>
</body></html>
</xsl:template>
<xsl:template match="expanded">
<tr>
<td><xsl:apply-templates/></td>
<xsl:apply-templates select='following-sibling::abbre[1]'/>
</tr>
</xsl:template>
<xsl:template match="abbre">
<td><xsl:apply-templates/></td>
</xsl:template>
</xsl:stylesheet>
By using small templates that are applied, you simplify your stylesheet. Additionally, there is no real reason to use xsl:value-of here - the built-in templates will do the right thing. You will end up with simpler templates that are easier to understand.