xslt import/include 2 files with the same template - xslt

I've got an interesting question on XSLT import/include.
I have 2 XSLT files with the same rule.
Receipt XSLT: (is run by itself)
<xsl:template match="Booking" mode="extraStyle">
<link rel="stylesheet" href="../css/receipt.css" type="text/css" media="screen"/>
</xsl:template>
EmailCommon XSLT: (serves as template library for Email docs, isn't run by itself)
<xsl:template match="Booking" mode="extraStyle">
<link rel="stylesheet" href="../css/email.css" type="text/css" media="screen"/>
</xsl:template>
So that depending on the document type I insert correct CSS files.
What I'm trying to do is to include these two documents into yet another XSLT:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl"
version="1.0">
<xsl:include href="receipt.xsl"/>
<xsl:include href="email.xsl"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
Nevertheless, because the rules are the same in both included stylesheets it boils down to the Last-in-first rule and I end up only including email.css.
I was wondering if something smart could be done in this case?
The only thing I was thinking is to using different mode, but then it wouldn't be as intuitive, rather then accumalate the code of all identical rules. Don't know how and whether at all it could be done in XSLT.
Thanks for help!
P.S. Sorry, I'm really trying to understand the formating rules on this site, but I simply can't :( gggrrr

I think making the template modes different is your best option.
<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:include href="receipt.xsl"/>
<xsl:include href="email.xsl"/>
<xsl:template match="Booking">
<xsl:apply-templates select="." mode="extraStyleReceipt" />
<xsl:apply-templates select="." mode="extraStyleEmail" />
</xsl:template>
</xsl:stylesheet>

Related

Choose XSL transform acoording to document content

I have a large number of XHTML documents which are created by different publishers, determined by a meta tag:
<meta name="citation_publisher" content="ACME publisher"/>
or in a different document
<meta name="citation_publisher" content="BETA publisher"/>
etc.
I have written stylesheets (about 1 page each) such as,
acme.xsl
beta.xsl
etc.
However I do not know the name of the publisher until I read the XHTML file.
It is possible, though very messy, to write a gigantic stylesheet of the form:
<xsl:choose>
<xsl:when test="$publisher='ACME publisher'">
<!-- acme.xsl sheet-->
</xsl:when>
<xsl:when test="$publisher='BETA publisher'">
<!-- beta.xsl sheet-->
</xsl:when>
</xsl:choose>
but there are at least 100 XSL files.
Is there any way, in XSL1, to select the stylesheet chunk according to the publisher? It would be nice to have the stylesheets as separate files and <xsl:import> them rather than have a single huge file.
UPDATE:
I think #Dimitre has answered the question correctly (and so I have accepted). I suspect that #MichaelKay's is actually better , but it does depend on having a pipeline managing the transformer. I shall try the <xsl:include> as a prototype and see whether it has downsides.
I wouldn't attempt to do this within a single XSLT stylesheet. It sounds to me like a good candidate for XProc, or some similar pipeline technology (e.g. Orbeon). Step 1, use XPath to classify the document, Step 2, transform it using the stylesheet chosen according to the results of Step 1.
but there are at least 100 XSL files. Is there any way, in XSL1, to
select the stylesheet chunk according to the publisher? It would be
nice to have the stylesheets as separate files and <xsl:import> them
rather than have a single huge file.
Here is one way to do this (I am showing working just with two content publisher types and this can be done for as many as needed):
Primary stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="unknown.xsl"/>
<xsl:import href="acme.xsl"/>
<xsl:import href="beta.xsl"/>
</xsl:stylesheet>
acme.xsl:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*[meta[#content='ACME publisher']]">
<xsl:value-of select="x * y"/>
</xsl:template>
</xsl:stylesheet>
beta.xsl:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*[meta[#content='BETA publisher']]">
<xsl:value-of select="x + y"/>
</xsl:template>
</xsl:stylesheet>
unknown.xsl:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<xsl:message terminate="yes">Error: Unknown content source</xsl:message>
</xsl:template>
</xsl:stylesheet>
When the transformation specified in the primary stylesheet is applied on this XML document:
acme.xml:
<t>
<meta name="citation_publisher" content="ACME publisher"/>
<x>6</x>
<y>4</y>
</t>
the wanted, correct result (x*y) is produced:
24
When the same transformation is applied on this XML document:
beta.xml:
<t>
<meta name="citation_publisher" content="BETA publisher"/>
<x>6</x>
<y>4</y>
</t>
again the correct result (x+y) is produced:
10
Finally, when the same transformation is applied on this XML document:
other.xml:
<t>
<meta name="citation_publisher" content="OTHER publisher"/>
<x>6</x>
<y>4</y>
</t>
the result of the transformation is the wanted termination with error message:
Error: Unknown content source
Processing terminated by xsl:message at line 5

Clientside XSLT in Mozilla/Firefox fails

I have a result document that renders in Chrome, but not Mozilla/Firefox.
I believe it is because there is top level leading whitespace (two blank lines before the <!DOCTYPE html).
How can I change this transform to not have leading whitespace (fiddle)?
XML:
<?xml-stylesheet href="/css/my.xsl" type="text/xsl"?>
<webpage>
<title>Book</title>
<auth>Mike</auth>
<container-content>
<p>foo1</p>
<p>foo2</p>
</container-content>
</webpage>
XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="1.0">
<xsl:output
method="xml"
indent="yes"
encoding="UTF-8"
omit-xml-declaration="yes"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"/>
<xsl:strip-space elements="*"/>
<xsl:template match="text()"/>
<xsl:template match="/">
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=utf-8"/>
</head>
<body>
<xsl:copy-of select="//container-content/*"/>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Result:
- a blank line here -
- and here -
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>
<body>
<p>foo1</p>
<p>foo2</p>
</body>
</html>
Alternatively, I may be incorrect, and the two blank lines are not the cause of the failed render in Mozilla/Firefox. I have a hard time troubleshooting client side transforms.
Side note: I've developed in Saxon 6.5, thinking Saxon best approximates what browsers do. I could be wrong. I note Xalan does not put in leading whitespace.
I just ran your stylesheet with Saxon 6.5 and indeed, it outputs two blank lines, which are removed if you change the xsl:output to be without indentation and with xml declaration. However, I believe this to be a bug in Saxon 6.5 (a small one, as the whitespace is not significant).
Running it with other XSLT 1.0 processors show no whitepace. However, as said in my comment, the whitespace is insignificant, as browsers do not serialize anyway. (note: apparently, browsers do some kind of serialization, in the sense that they look to whether you use XML or HTML output).
I ran your example with Firefox and it "just works". Since your stylesheet does a simple copy of the XML, it shows just the text. If I change the xsl:output to HTML and add a few lines to be sure I am running it correctly (I added an <h1>Hello</h1>, it shows the HTML.
I'm not sure what you expect the browser to show, but my guess is not XML, but (X)HTML. XSLT 1.0 is not very good with XHTML (it is supported in XSLT 2.0, but that is not supported by browsers), but works fine with HTML.
I modified your stylesheet as follows:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:template match="text()"/>
<xsl:template match="/">
<html>
<head />
<body>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="title">
<h1><xsl:value-of select="."/></h1>
</xsl:template>
<xsl:template match="auth">
<p>Author: <xsl:value-of select="." /></p>
</xsl:template>
</xsl:stylesheet>
And in Firefox and Chrome it renders as follows:
Note (1): if you do not run it from a web server (either local or remote), it will not run in either Firefox or Chrome because of security restrictions.
Note (2): to view the rendered XML or HTML, use the Inspect Element feature of the developer tools of either Chrome or Firefox.
Note (3): you do not need to use the meta-tag, as the specification requires this meta tag to be output as soon as it recognizes that HTML is output.
Note (4) if you are unsure whether or not Firefox is loading your stylesheet correctly, have a look using Firebug, it should show something like this (mark the "200 OK"):
If you want to transform to XHTML then you need to make sure you use the XHTML namespace for your result elements so put
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/1999/xhtml"
version="1.0">
on your stylesheet, as otherwise with output method xml your elements in no namespace are not recognized as XHTML elements by Mozilla.
As your input p elements are also in no namespace you can not copy them through but have to write a template for them
<xsl:template match="*">
<xsl:element name="{local-name()}"><!-- assumes you have the namespace declaration suggested above -->
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
and then use <xsl:apply-templates select="//container-content/*"/> instead of the copy-of. And in that case the <xsl:template match="text()"/> needs to be removed as otherwise the text of the transformed p elements would not show up.

How to create hyperlink using XSLT?

I'm new at XSLT. I want to create a hyperlink using XSLT.
Should look like this:
Read our privacy policy.
"privacy policy" is the link and upon clicking this, should redirect to example "www.privacy.com"
Any ideas? :)
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<html>
Read our <b>privacy policy.</b>
</html>
</xsl:template>
</xsl:stylesheet>
when applied on any XML document (not used), produces the wanted result:
<html>Read our <b>privacy policy.</b></html>
and this is displayed by the browser as:
Read our privacy policy.
Now imagine that nothing is hardcoded in the XSLT stylesheet -- instead the data is in the source XML document:
<link url="www.privacy.com">
Read our <b>privacy policy.</b>
</link>
Then this 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:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="link">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
when applied on the above XML document, produces the wanted, correct result:
<a href="www.privacy.com">
Read our <b>privacy policy.</b>
</a>
If you want to read hyperlink value from a XML file, this should work:
Assumption: href is an attribute on specific element of your XML.
<xsl:variable name="hyperlink"><xsl:value-of select="#href" /></xsl:variable>
<xsl:value-of select="#href" />
If you want to have hyperlinks in XSLT, then you need to create HTML output using XSLT.
In HTML you can create a hyperlink like this
Read our privacy policy.
In this the whole text becomes a hyperlink pointing to www.yourwebsite.com

XSL namespaces and xsl:for-each

I'm currently stuck trying to use XSL to transform a XML document into HTML. The XML document uses namespaces and I don't really have too much experience with XSL let alone namespaces. Basically all I want to do is grab every instance of s:treatment and output this as a list. I've changed data so not to expose the website I'm doing this for. I'm using Classic ASP (can't update to ASP.NET) to transform the XML on the server, so the XSL has to be version 1 :(
Any help would be really appreciated here as I just can't figure out what's going wrong.
Here's the XML:
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title type="text">a</title>
<id>a</id>
<rights type="text">© Crown Copyright 2009</rights>
<updated>2011-01-19T11:23:25Z</updated>
<category term="Service"/>
<author>
<name>c</name>
<uri>http://www.meh.com</uri>
<email>erwt</email>
</author>
<complete xmlns="http://purl.org/syndication/history/1.0"/>
<entry>
<id>http://meh.com/services</id>
<title type="text">title</title>
<updated>2010-06-18T19:52:12+01:00</updated>
<link rel="self" title="title" href="meh"/>
<link rel="alternate" title="title" href="id"/>
<content type="application/xml">
<s:service xmlns:s="http://syndication.nhschoices.nhs.uk/services">
<s:type code="S">h</s:type>
<s:deliverer>j</s:deliverer>
<s:parent>k</s:parent>
<s:treatments>
<s:treatment>fissure</s:treatment>
<s:treatment>fistula</s:treatment>
<s:treatment>liver</s:treatment>
<s:treatment>pancreas</s:treatment>
<s:treatment>Cirrhosis</s:treatment>
<s:treatment>Coeliac disease</s:treatment>
<s:treatment>Crohn's disease</s:treatment>
<s:treatment>Diagnostic endoscopy of the stomach</s:treatment>
<s:treatment>Diverticular problems</s:treatment>
<s:treatment>Gastrectomy</s:treatment>
<s:treatment>Gastroenteritis</s:treatment>
<s:treatment>Gastroenterology</s:treatment>
<s:treatment>Gastroesophageal reflux disease(GORD)</s:treatment>
<s:treatment>Hepatitis</s:treatment>
<s:treatment>Hepatitis A</s:treatment>
<s:treatment>Hepatitis B</s:treatment>
<s:treatment>Hepatitis C</s:treatment>
<s:treatment>Hernia hiatus</s:treatment>
<s:treatment>Ileostomy</s:treatment>
<s:treatment>Irritable bowel syndrome</s:treatment>
<s:treatment>Liver disease (alcoholic)</s:treatment>
<s:treatment>Obesity</s:treatment>
<s:treatment>Pancreatitis</s:treatment>
<s:treatment>Peptic ulcer</s:treatment>
<s:treatment>Peritonitis</s:treatment>
<s:treatment>Primary biliary cirrhosis</s:treatment>
<s:treatment>Surgery for haemorrhoids</s:treatment>
<s:treatment>Therapeutic endoscopy on the stomach</s:treatment>
<s:treatment>Ulcerative colitis</s:treatment>
</s:treatments>
<s:phone>020 8</s:phone>
<s:fax>020 8</s:fax>
<s:email>jj</s:email>
<s:website>oiyi</s:website>
</s:service>
</content>
</entry>
</feed>
As you can see it uses the Atom namespace, Purl syndication namespace and NHS Choices namespace only the NHS Choices namespace actually uses the prefix though, this is what is confusing me really. How would I declare the other namespaces and do I even need to?
Here's my XSL:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:s="http://syndication.nhschoices.nhs.uk/services">
<ul>
<xsl:template match="/">
<xsl:for-each select="feed/entry/content/s:service/s:treatments/s:treatment">
<li><xsl:text></xsl:text></li>
</xsl:for-each>
</xsl:template>
</ul>
</xsl:stylesheet>
This XSL was taken from the w3schools example so apologies if it's bare.
Any ideas what I need to do to make this work?
Thanks,
Colin
First of all your XSLT is not valid at all (uloutside xsl:template is not valid XSL).
Further more as there is a default namespace on your feed tag you have also to define this in your xslt. You also better make usage of mathcing templates intead of for-each loops in CSLT.
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:s="http://syndication.nhschoices.nhs.uk/services"
xmlns:a="http://www.w3.org/2005/Atom" exclude-result-prefixes="a s">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<ul>
<xsl:apply-templates
select="a:feed/a:entry/a:content/s:service/s:treatments/s:treatment" />
</ul>
</xsl:template>
<xsl:template match="s:treatment">
<li>
<xsl:value-of select="." />
</li>
</xsl:template>
</xsl:stylesheet>

xsltproc doesn't select elements by name

I am trying to transform XHTML using an XSLT stylesheet, but I can't even get a basic stylesheet to match anything. I'm sure I'm missing something simple.
Here's my XHTML source document (no big surprises):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="generator" content=
"HTML Tidy for Windows (vers 25 March 2009), see www.w3.org" />
...
</body>
</html>
The actual contents don't matter too much, as I'll demonstrate below. By the way, I'm pretty sure the document is well-formed since it was created via tidy -asxml.
My more complex XPath expressions were not returning any results, so as a sanity test, I'm trying to transform it very simply using the following stylesheet:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" omit-xml-declaration="yes" indent="no"/>
<xsl:template match="/">
<xsl:text>---[</xsl:text>
<xsl:for-each select="html">
<xsl:text>Found HTML element.</xsl:text>
</xsl:for-each>
<xsl:text>]---</xsl:text>
</xsl:template>
</xsl:stylesheet>
The transform is done via xsltproc --nonet stylesheet.xsl input.html, and the output is: "---[]---" (i.e., it didn't find a child element of html). However, if I change the for-each section to:
<xsl:for-each select="*">
<xsl:value-of select="name()"/>
</xsl:for-each>
Then I get "---[html]---". And similarly, if I use for-each select="*/*" I get "---[headbody]---" as I would expect.
Why can it find the child element via * (with name() giving the correct name) but it won't find it using the element name directly?
The html element in your source XML defines a namespace. You have to include it in your match expression and reference it in your xsl:stylesheet element:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:html="http://www.w3.org/1999/xhtml">
<xsl:output method="text" omit-xml-declaration="yes" indent="no"/>
<xsl:template match="/">
<xsl:text>---[</xsl:text>
<xsl:for-each select="html:html">
<xsl:text>Found HTML element.</xsl:text>
</xsl:for-each>
<xsl:text>]---</xsl:text>
</xsl:template>
</xsl:stylesheet>
Change your stylesheet from:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" omit-xml-declaration="yes" indent="no"/>
<xsl:template match="/">
<xsl:text>---[</xsl:text>
<xsl:for-each select="html">
<xsl:text>Found HTML element.</xsl:text>
</xsl:for-each>
<xsl:text>]---</xsl:text>
</xsl:template>
</xsl:stylesheet>
to:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:x="http://www.w3.org/1999/xhtml"
>
<xsl:output method="text" omit-xml-declaration="yes" indent="no"/>
<xsl:template match="/">
<xsl:text>---[</xsl:text>
<xsl:for-each select="x:html">
<xsl:text>Found HTML element.</xsl:text>
</xsl:for-each>
<xsl:text>]---</xsl:text>
</xsl:template>
</xsl:stylesheet>
Explanation:
The XML document has declared a default namespace: "http://www.w3.org/1999/xhtml", and all unprefixed nodes that descend from the top element declaring this default namespace, belong to this namespace.
On the other side, in XPath any unprefixed name is considered to belong in "no namespace".
Therefore, the <xsl:for-each select="html"> instruction will select and apply its body to all html elements that belong to "no namespace" -- and there are none such in the document -- the only html element does belong to the xhtml namespace.
Solution:
The the names that belong to a default namespace cannot be referenced unprefixed. Therefore, we need to bind a prefix to the namespace such an element belongs to. If this prefix is "x:", then we can reference any such element prefixed with "x:".
A workaround without declaring the namespace, so that the stylesheet accept any namespace:
<xsl:template match="*[name()='html']" >