How to include an XSL file into another? - xslt

I use XSLT Version 2.0 and I want to include a head template head.xsl in another file home.xsl.
home.xsl :
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css"/>
<title/>
</head>
<body>
<div id="main">
<div id="content">
<xsl:call-template name="home-in"/>
</div>
</div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
head.xsl :
xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css"/>
<title/>
</head>
<body>
<div id="container">
<div id="header">
<div id="menu">
<ul>
<li>Home</li>
<li>about</li>
</ul>
</div>
</div>
</div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
How can I include my head.xsl file into home.xsl ?

You can include the other file using <xsl:include>
Your home.xsl file would look like this
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="html" indent="yes"/>
<xsl:include href="head.xsl"/>
<xsl:template match="/">
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css"/>
<title/>
</head>
<body>
<div id="main">
<div id="content">
<xsl:call-template name="home-in"/>
</div>
</div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
More details
The XSLT processor will simply replace the instruction with the content of the stylesheet named in the href attribute. Note that an included stylesheet template will have the same default priorities and import precedence as the including stylesheet.
Don't confuse xsl:include with xsl:import, which is similar except that instructions in the imported stylesheet can be be overridden by instructions in the importing stylesheet and in any included stylesheet. In other word, the import precedence of elements in an imported stylesheet is always less than that of the importing stylesheet.
Since using xsl:include is the same as copying the code in the file, make sure you don't have duplicate template name when using named templates.
Here a paste from the w3 documentation
Including a stylesheet multiple times can cause errors because of
duplicate definitions. Such multiple inclusions are less obvious when
they are indirect. For example, if stylesheet B includes stylesheet A,
stylesheet C includes stylesheet A, and stylesheet D includes both
stylesheet B and stylesheet C, then A will be included indirectly by D
twice. If all of B, C and D are used as independent stylesheets, then
the error can be avoided by separating everything in B other than the
inclusion of A into a separate stylesheet B' and changing B to contain
just inclusions of B' and A, similarly for C, and then changing D to
include A, B', C'.
Note that both xsl:include and xsl:import elements are only allowed as top-level element.

Related

XSL embed original XML file

I am transforming XML files with XSL to HTML files. Is it possible to embed the original XML file in the HTML output? When yes, how is that possible?
Update 1: To make my need better understandable: In my HTML file, I want a form where I can download the original XML file. Therefore I have to embed the original XML file into my HTML file (e.g. as a hidden input field)
Thanks
If you want to copy the nodes through you can simply do <xsl:copy-of select="/"/> where you want to insert them, however, putting arbitrary XML nodes into HTML does not make sense usually. If you want to serialize an XML document to plain text to render it then you can use solutions like http://lenzconsulting.com/xml-to-string/, for instance:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:import href="http://lenzconsulting.com/xml-to-string/xml-to-string.xsl"/>
<xsl:output method="html" doctype-public="XSLT-compat" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<html>
<head>
<title>Test</title>
</head>
<body>
<section>
<h1>Test</h1>
<xsl:apply-templates/>
<section>
<h2>Source</h2>
<pre>
<xsl:apply-templates mode="xml-to-string"/>
</pre>
</section>
</section>
</body>
</html>
</xsl:template>
<xsl:template match="data">
<ul>
<xsl:apply-templates/>
</ul>
</xsl:template>
<xsl:template match="item">
<li>
<xsl:apply-templates/>
</li>
</xsl:template>
</xsl:transform>
transforms an XML input like
<data>
<item att="value">
<!-- comment -->
<foo>bar</foo>
</item>
</data>
into the HTML
<!DOCTYPE html
PUBLIC "XSLT-compat">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test</title>
</head>
<body>
<section>
<h1>Test</h1>
<ul>
<li>
bar
</li>
</ul>
<section>
<h2>Source</h2><pre><data>
<item att="value">
<!-- comment -->
<foo>bar</foo>
</item>
</data></pre></section>
</section>
</body>
</html>

XSLT Order classes in a non-alphabetical non-numerical order

I have researched this problem but the suggestions that I have found seems to be rather convoluted and for a more general scenario. Perhaps there is a more concise solution for this scenario, that is more specific.
I have a large number of html files like the following:
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
<title>t</title>
</head>
<body>
<div class="a">
<div class="f">f1</div>
<div class="e">e1</div>
<div class="e">e2</div>
<div class="g">g</div>
<div class="c">c1</div>
<div class="b">
<div class="ba">ba</div>
<div class="bb">bb</div>
</div>
<div class="c">c2</div>
<div class="f">f2</div>
<div class="d">d</div>
<div class="c">c3</div>
</div>
...
</body>
</html>
Rule # 1
I want to order the div's inside div class="a" in a specific order of their class attribute that is non-alphabetic and non-numeric. For the purpose of this example, let's the final order be the following:
g
f
b
c
e
d
In my real examples, the list is much longer.
Rule # 2
If for a given class attribute there is more than one node, then they should be left in the same order as in the original file, for instance:
c1
c2
c3
Please notice that in my real examples these values would not be in alphanumerical order.
Rule # 3
The order of child nodes must not be affected, for instance:
ba
bb
Please notice that in my real examples these values would not be in alphanumerical order either.
The final output should be like the following:
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
<title>t</title>
</head>
<body>
<div class="a">
<div class="g">g</div>
<div class="f">f1</div>
<div class="f">f2</div>
<div class="b">
<div class="ba">ba</div>
<div class="bb">bb</div>
</div>
<div class="c">c1</div>
<div class="c">c2</div>
<div class="c">c3</div>
<div class="e">e1</div>
<div class="e">e2</div>
<div class="d">d</div>
</div>
...
</body>
</html>
I have thought at first to:
Prepend a number to the class attribute value, for instance rename class="g" to class="01g", etc
Order the classes in alphanumerical order
Remove the number, for instance rename class="01g" to class = "g", etc
However I dislike this solution because it requires too many transformations.
What I would really like is to come up with a more elegant solutions. Perhaps I would define an ordered list of class values and a clever index would somehow put the nodes in that defined order?
Do you have any suggestions to add to my xslt template?
<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:stylesheet>
AFAICT, you want to do something like:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" omit-xml-declaration="yes" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="div[#class='a']">
<xsl:variable name="sort-order">gfbced</xsl:variable>
<xsl:copy>
<xsl:apply-templates select="#*|node()">
<xsl:sort select="string-length(substring-before($sort-order, #class))" data-type="number" order="ascending"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
To accommodate class values that are not single characters, you can use:
<xsl:template match="div[#class='a']">
<xsl:variable name="sort-order">|g|f|b|c|e|d|</xsl:variable>
<xsl:copy>
<xsl:apply-templates select="#*|node()">
<xsl:sort select="string-length(substring-before($sort-order, concat('|', #class, '|')))" data-type="number" order="ascending"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>

apply-templates outputs content more times than expected

I'm new to XSLT and I can't understand why the root get processed twice (at least this is my interpretation of this output).
EDIT: (I'm using Saxon-HE with XSLT 2.0) but also tested with several online processes, getting always the same result.
XSLT file
<?xml version="1.0" encoding="UTF-8"?>
<!-- XResume.xsl: resume.xml ==> resume.xhtml -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="https://github.com/IME-SE8/XResume">
<xsl:output method="html"/>
<xsl:template match="/">
<html>
<head>
<meta charset="utf-8" />
<meta lang="en" />
<meta name="description" content="Personal Resume and Portfolio" />
<title><xsl:value-of select="resume/personalInformation/name/attribute::shortForm" /> Website</title>
</head>
<body>
<xsl:apply-templates select="resume"/>
</body>
</html>
</xsl:template>
<xsl:template match="resume">
<div class="resume">
<div class="header">
<div class="name"><xsl:value-of select="personalInformation/name" /></div>
<div class="contacts">
<xsl:for-each select="personalInformation/contact">
<div class="contactInformation">
<p><xsl:value-of select="organization" /></p>
<p><xsl:value-of select="address" /></p>
<p><xsl:value-of select="phoneNumber" /></p>
<p><xsl:value-of select="email" /></p>
</div>
</xsl:for-each>
</div>
</div>
<div class="sections">
<xsl:apply-templates />
</div>
</div>
</xsl:template>
<xsl:template match="interests"></xsl:template>
<xsl:template match="education"></xsl:template>
<xsl:template match="skills"></xsl:template>
<xsl:template match="experiences"></xsl:template>
<xsl:template match="projects"></xsl:template>
<xsl:template match="awards"></xsl:template>
</xsl:stylesheet>
XML file
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl"
href="https://github.com/IME-SE8/XResume/master/XResume.xsl"?>
<resume
xmlns="https://github.com/IME-SE8/XResume"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://github.com/IME-SE8/XResume XResume.xsd">
<personalInformation>
<name first="John" last="Doe" shortForm="JD">John Doe</name>
<contact type="institutional">
<organization>StackOverflow Institute of Technology</organization>
<address>Internet</address>
<phoneNumber>+1 (666) 666-9999</phoneNumber>
<email>john#d.oe</email>
</contact>
</personalInformation>
<interests>
<interest>Q and A</interest>
<interest>XSLT</interest>
</interests>
<education></education>
<skills></skills>
<experiences></experiences>
<projects></projects>
<awards></awards>
</resume>
HTML output
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<meta lang="en">
<meta name="description" content="Personal Resume and Portfolio">
<title>JD Website</title>
</head>
<body>
<div class="resume">
<div class="header">
<div class="name">John Doe</div>
<div class="contacts">
<div class="contactInformation">
<p>StackOverflow Institute of Technology</p>
<p>Internet</p>
<p>+1 (666) 666-9999</p>
<p>john#d.oe</p>
</div>
</div>
</div>
<div class="sections">
John Doe
StackOverflow Institute of Technology
Internet
+1 (666) 666-9999
john#d.oe
</div>
</div>
</body>
</html>
(yes, with that amount of blank lines)
The output header div is perfectly fine, but inside the sections div that apply-templates renders all the information in the div header again but without the HTML tags.
Is there any XSLT processing detail am I missing? Does the template match sets the context in a way that the matched element is now considered a root or something like that?
The problem is here:
<div class="sections">
<xsl:apply-templates />
</div>
This applies templates to all child nodes of the current node (resume), including the personalInformation element.
As there is no matching template specified for personalInformation, the builtin XSLT templates are used by the XSLT processor and applying them results in outputting the concatenation of all descendent text-nodes of the personalInformation element.
Solution:
Replace:
<div class="sections">
<xsl:apply-templates />
</div>
with:
<div class="sections">
<xsl:apply-templates select="*[not(self::personalInformation)]" />
</div>
The result of the transformation now doesn't contain the noted problematic output:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<meta lang="en">
<meta name="description" content="Personal Resume and Portfolio">
<title>JD Website</title>
</head>
<body>
<div class="resume">
<div class="header">
<div class="name">John Doe</div>
<div class="contacts">
<div class="contactInformation">
<p>StackOverflow Institute of Technology</p>
<p>Internet</p>
<p>+1 (666) 666-9999</p>
<p>john#d.oe</p>
</div>
</div>
</div>
<div class="sections"></div>
</div>
</body>
</html>
You haven't supplied an expected output, so I will guess at your intended outcome. Here is an XSLT 2.0 solution. If you need XSLT 1.0, please comment, and I can add. But just remember that if your transform engine is the browser, you have no excuse not to use XSLT 2.0. (Refer Saxon CE).
XSLT 2.0 Solution
This XSLT 2.0 stylesheet ...
<xsl:transform
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:r="https://github.com/IME-SE8/XResume"
exclude-result-prefixes="r"
version="2.0">
<xsl:output method="html" version="5" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="/">
<html>
<head>
<meta lang="en" />
<meta name="description" content="Personal Resume and Portfolio" />
<title><xsl:value-of select="r:resume/r:personalInformation/r:name/#shortForm" /> Website</title>
</head>
<body>
<xsl:apply-templates select="r:resume"/>
</body>
</html>
</xsl:template>
<xsl:template match="r:resume">
<div class="resume">
<div class="header">
<div class="name"><xsl:value-of select="r:personalInformation/r:name" /></div>
<div class="contacts">
<xsl:apply-templates select="r:personalInformation/r:contact" />
</div>
</div>
<div class="sections">
<xsl:apply-templates select="* except r:personalInformation" />
</div>
</div>
</xsl:template>
<xsl:template match="r:contact">
<div class="contactInformation">
<xsl:apply-templates />
</div>
</xsl:template>
<xsl:template match="r:organization|r:address|r:phoneNumber|r:email">
<p><xsl:value-of select="." /></p>
</xsl:template>
<xsl:template match="r:education|r:skills|r:experiences|r:projects|r:awards">
<h2><xsl:value-of select="local-name()" /></h2>
<p><xsl:value-of select="." /></p>
</xsl:template>
<xsl:template match="r:interests">
<h2>interests</h2>
<ul>
<xsl:apply-templates />
</ul>
</xsl:template>
<xsl:template match="r:interest">
<li>
<xsl:value-of select="." />
</li>
</xsl:template>
<xsl:template match="*" />
</xsl:transform>
... when applied to this input document ...
<resume
xmlns="https://github.com/IME-SE8/XResume"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://github.com/IME-SE8/XResume XResume.xsd">
<personalInformation>
<name first="John" last="Doe" shortForm="JD">John Doe</name>
<contact type="institutional">
<organization>StackOverflow Institute of Technology</organization>
<address>Internet</address>
<phoneNumber>+1 (666) 666-9999</phoneNumber>
<email>john#d.oe</email>
</contact>
</personalInformation>
<interests>
<interest>Q and A</interest>
<interest>XSLT</interest>
</interests>
<education></education>
<skills></skills>
<experiences></experiences>
<projects></projects>
<awards></awards>
</resume>
... will yield this output html page ....
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta lang="en">
<meta name="description" content="Personal Resume and Portfolio">
<title>JD Website</title>
</head>
<body>
<div class="resume">
<div class="header">
<div class="name">John Doe</div>
<div class="contacts">
<div class="contactInformation">
<p>StackOverflow Institute of Technology</p>
<p>Internet</p>
<p>+1 (666) 666-9999</p>
<p>john#d.oe</p>
</div>
</div>
</div>
<div class="sections">
<h2>interests</h2>
<ul>
<li>Q and A</li>
<li>XSLT</li>
</ul>
<h2>education</h2>
<p></p>
<h2>skills</h2>
<p></p>
<h2>experiences</h2>
<p></p>
<h2>projects</h2>
<p></p>
<h2>awards</h2>
<p></p>
</div>
</div>
</body>
</html>
Explanation: Why were you getting root processed twice
In short because your <div class="sections"><xsl:apply-templates /></div> instruction did not specify a select attribute. The default selection applied, which was at that point the document root.
All the elements in your source document are in a namespace, but your stylesheet is written to process elements in no namespace. Welcome to the club, and join the 10m other people who have fallen into this trap. Essentially, you resume elements don't match the match="resume" template, so the default template kicks in, and this outputs the raw text with no tags. For the solution, search on "XSLT default namespace" and choose any one of about 1000 answers.
On re-reading, I see that you've used xpath-default-namespace="https://github.com/IME-SE8/XResume", which should fix the problem if you are using an XSLT 2.0 processor, or trigger an error if you're using an XSLT 1.0 processor. So it might be useful (actually, it's always useful) to tell us what processor you are using and how you are running it.

Generate html from xsl without DOCTYPE

Is it possible to generate html-output with xsl that has no doctype added to the output? If I don´t set any doctype myself it produces one on its own.
EDIT :
Since I don´t think that this is possible, I solved my problem bysimply cutting away the DOCTYPE after the html is generated with followiing regex: '<&!DOCTYPE[^>]*>'
Quickly tested with Saxon, yes this is possible...I'm not sure what xslt library you're using so it could be a symptom of that.
If I use Saxon to run this transform against ANY xml file (which generates a minimum viable HTML5 document, minus DOCTYPE) :
<?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" encoding="iso-8859-1" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<title>Test</title>
</head>
<body>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
I get this output :
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Test</title>
</head>
<body></body>
</html>

Conditionally selecting node not working

I'm just trying to select the node 'productgroep' with attribute value 'cd'. This is not working and I really don't understand why, I searched for answers but didn't find any.
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="Oefening_8.xsl"?>
<catalogus>
<!-- cd catalogus -->
<productgroep type="cd">
<item referentienummer="7051444" catalogusnummer="1800022" EAN="0025218000222">
...
</productgroep>
<productgroep type="film">
<item referentienummer="8051445" catalogusnummer="2800023" EAN="5051888073650">
....
</productgroep>
</catalogus
XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:template match="/">
<html>
<head>
<title>Oefening_8.xsl</title>
<meta charset="utf-8"/>
<link href="Oefening_8.css" type="text/css" rel="stylesheet"/>
</head>
<body>
<h1></h1>
<xsl:template match="productgroep[#type='cd']">
</xsl:template>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
An <xsl:template/> cannot be a child of a <xsl:template/>, so your stylesheet is currently invalid and probably giving an error somewhere, depending on how you are using the XML and XSL.
One solution is to create the separate <xsl:template>s and use <xsl:apply-templates /> to processes the children of the source element.
<xsl:template match="/">
<html>
<head>
<title>Oefening_8.xsl</title>
<meta charset="utf-8"/>
<link href="Oefening_8.css" type="text/css" rel="stylesheet"/>
</head>
<body>
<h1></h1>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="productgroep[#type='cd']">
<xsl:value-of select="item/#catalogusnummer"/> <!-- print #catalogusnummer for example -->
</xsl:template>
As #andyb pointed out, you can't have a template inside a template. It may be that you meant to use xsl:apply-templates where you have xsl:template, but that wouldn't have worked either with the path you used, because the current context there is the node above catalogus. Your options are to change the initial xsl:template to select the root element with either:
or
or to use the full path in the xsl:apply-templates:
I prefer the first option:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:template match="/*">
<html>
<head>
<title>Oefening_8.xsl</title>
<meta charset="utf-8"/>
<link href="Oefening_8.css" type="text/css" rel="stylesheet"/>
</head>
<body>
<h1></h1>
<xsl:apply-templates select="productgroep[#type='cd']" />
</body>
</html>
</xsl:template>
</xsl:stylesheet>