XSLT to transform a XHTML document - xslt

I am new to XSLT but it was suggested to me as a way to accomplish a specific task. I have a bunch of xhtml files that I would like to remove a sidebar from. The sidebar is contained in a <div class="foo"> element.
I can successfully perform the identity transform using the instructions in this answer. But I can't seem to match the elements I am wanting to remove. Perhaps this because they are not top level elements like they are in every example of this design pattern I find?
Can someone explain the correct way to remove <div class="foo"> and all its children from the identity transform?

It is quite likely that your problem arises due to the presence of a default (xhtml) namespace in the source XHTML file (which you have not shown to us, so this is a guess at best).
Can someone explain the correct way to
remove and all its
children from the identity transform?
Here is how to do this in case a default namespace is present:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<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="xhtml:div[#class='foo']"/>
</xsl:stylesheet>
When this transformation is applied on the following XHTML document:
<html xmlns="http://www.w3.org/1999/xhtml">
<div class="class1">
<p>Text1</p>
</div>
<div class="foo">
<p>Text foo</p>
</div>
<div class="class2">
<p>Text2</p>
</div>
</html>
the wanted, correct result is produced:
<html xmlns="http://www.w3.org/1999/xhtml">
<div class="class1">
<p>Text1</p>
</div>
<div class="class2">
<p>Text2</p>
</div>
</html>
Using a namespace prefix in the match expression of the template is necessary, because XPath considers any unprefixed name in "no namespace" and a match expression with non-prefixed names does not match any nodes, because it specifies nodes in "no namspace", but all the nodes of the source document are in the XHTML namespace.
In case there is no default namespace in the source document, the transformation can be simplified:
When this transformation is applied on the following XML document (note that it doesn't define a default namespace):
<html>
<div class="class1">
<p>Text1</p>
</div>
<div class="foo">
<p>Text foo</p>
</div>
<div class="class2">
<p>Text2</p>
</div>
</html>
the wanted, correct result is produced:
<html>
<div class="class1">
<p>Text1</p>
</div>
<div class="class2">
<p>Text2</p>
</div>
</html>
Both transformation use the identity rule to copy any node of the document and another template, which overrides the identity rule for nodes matching "div[#class='foo']". This second template is empty (has no body), which means that the matched node and the subtree rooted in it are not processed at all (ignored) and thus will not appear in the output.

Related

XSL FO Pass dynamic value from the HTML to XSLT variable

I have trying to create pdf from html file using FOP.
My requirement is, I want to pass variable value at run time.
How can I pass variable value at run time?
It is not clear at what point you can inject "variables" nor how you expect to do them. Here's a sample that may provide some inspiration. It only uses a simple identity-translate and omits all the FO stuff for brevuty.
General principle -- put in a hidden div with some codes that are variables. For instance and simplicity, your input HTML now has this:
<html>
<div class="variables" style="display:none">
<div class="var_1" data-value="variable 1 value"/>
<div class="var_2" data-value="variable 2 value"/>
<div class="var_3" data-value="variable 3 value"/>
</div>
<div>
<div>Var 1 Value: <span class="variable" data-ref="var_1"/></div>
<div>Var 2 Value: <span class="variable" data-ref="var_2"/></div>
<div>Var 3 Value: <span class="variable" data-ref="var_3"/></div>
</div>
</html>
And you modify your XSL for a template that matches on a span where you want to insert the variable:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="span[#class='variable']">
<xsl:variable name="lookup">
<xsl:value-of select="#data-ref"/>
</xsl:variable>
<span>
<xsl:value-of select="//div[#class=$lookup]/#data-value"/>
</span>
</xsl:template>
<xsl:template match="node()">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The output of this would be:
<html>
<div class="variables" style="display:none">
<div class="var_1" data-value="variable 1 value"></div>
<div class="var_2" data-value="variable 2 value"></div>
<div class="var_3" data-value="variable 3 value"></div>
</div>
<div>
<div>Var 1 Value: <span>variable 1 value</span></div>
<div>Var 2 Value: <span>variable 2 value</span></div>
<div>Var 3 Value: <span>variable 3 value</span></div>
</div>
</html
Of course, you could expand that to include a template to strip the div whose class is variables for instance to not have it in the output or processed by your templates.
You can pass parameters to the XSLT stylesheet that generates the XSL-FO that is what FOP formats.
If you are using FOP to do the XSLT transformation, the format is -param name value (see https://xmlgraphics.apache.org/fop/2.3/running.html). If you are using an external XSLT processor to generate the XSL-FO that you pass to FOP, then you use the format that the XSLT processor requires (which will be specified in its documentation).
The closest things to variable text in the formatting stage are fo:marker and fo:table-marker, but even the markers are set before the formatting starts, and the variability comes from not knowing where page breaks occur until the document is formatted.

XSL Template-match replace element

I have scoured the forums and and still not clear on this. I'm very new to the topic.
I have HTML output that is being sent to a browser email client (Outlook). Outlook overwrites the characteristics of the <p> tag and introduces large spacing.
I would like to set up a template-match to replace all <p> tags with <div> or <span>.
For complicated reasons which will not be addressed in this post, I cannot stop the HTML from being rendered with <p> tags in it.
So lets say that I have:
<p xmlns="http://www.w3.org/1999/xhtml">
<span>Some text</span>
</p>
I would want the output to be
<span>Some text</span>
with the <p> tags removed.
If I have
<p xmlns="http://www.w3.org/1999/xhtml">
<b>Some other text</b>
</p>
then I would be happy with either:
<b>Some other text</b>
or
<span>
<b>Some other text</b>
</span>
Just as long as it gets rid of the <p> tags.
It would also need to recognize <p> without any attributes.
I thought about something like
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="p">
<span>
<xsl:apply-templates select="#*|node()" />
</span>
</xsl:template>
but this does not work. The <p> tags still appear.
It is possible to write an adapter which will intercept the HTML before it is sent to the smtp server and manipulate it, but there are considerable difficulties in this approach which I am looking to avoid.
Is it even possible to do what I am attempting? Any help greatly appreciated.
Your input documents, this sample for instance:
<p xmlns="http://www.w3.org/1999/xhtml">
<span>Some text</span>
</p>
Have a default namespace. And that's a good thing because a valid XHTML document must be in a namespace.
This means that this namespace applies to all elements in the document by default, and you have to account for this in your XSLT stylesheet. Redeclare this namespace there:
<xsl:stylesheet xmlns:xhtml="http://www.w3.org/1999/xhtml">
and whenever you make a reference to an element from the input document, prefix the element with xhtml::
<xsl:template match="xhtml:p">
<span>
<xsl:apply-templates select="#*|node()" />
</span>
</xsl:template>
This should get you started. You have not told us yet if the output document should also be in a namespace or not.
Currently, only modifying the two templates you already have, a structure like
<p>
<span/>
</p>
will end up as
<span>
<span/>
</span>
Is this acceptable for you? If not, there must be an additional rule (template) about p elements that contain span elements:
<xsl:template match="xhtml:p[xhtml:span]">
or perhaps
<xsl:template match="xhtml:p[//xhtml:span]">

How to include an XSL file into another?

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.

XSLT - How to evaluate a single node in multiple places

I am building an image rotator in XSLT that requires the following markup:
<div class="wrapper">
<div class="overlay"></div>
<div id="slider" class="slider">
[IMAGE FROM NODE A GOES HERE]
[IMAGE FROM NODE B GOES HERE]
...
</div>
<div id="htmlcaption" class="html-caption">
[CAPTION FOR NODE A GOES HERE]
[CAPTION FOR NODE B GOES HERE]
...
</div>
</div>
I need help constructing the XSLT so that Node A would get evaluated inside #slider then get re-evaluated in #htmlcaption, then Node B, and so on.
Any help would be greatly appreciated.
Thanks!
First, it is perfectly possible to evaluate a source element multiple times. Just use the same selector.
For example, considering the following XML:
<images>
<node id="a" image="foo.png" caption="foo" />
<node id="b" image="bar.png" caption="bar" />
</images>
This XSLT will repeatedly output stuff from the first node:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="images">
<div id="images">
<img><xsl:value-of select="node[#id='a']/#image"/></img>
<img><xsl:value-of select="node[#id='a']/#caption"/></img>
<img><xsl:value-of select="node[#id='a']/#image"/></img>
<img><xsl:value-of select="node[#id='a']/#caption"/></img>
</div>
</xsl:template>
Output:
<div id="images">
<img>foo.png</img>
<img>foo</img>
<img>foo.png</img>
<img>foo</img>
</div>
However it looks like what you really want is to loop over a bunch of nodes containing image and caption.
You could use a for-each loop to avoid selecting the nodes by name:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="images">
<div id="images">
<xsl:for-each select="node">
<img><xsl:value-of select="#image"/></img>
</xsl:for-each>
</div>
<div id="captions">
<xsl:for-each select="node">
<div><xsl:value-of select="#caption"/></div>
</xsl:for-each>
</div>
</xsl:template>
</xsl:stylesheet>
Which will produce:
<div id="images">
<img>foo.png</img>
<img>bar.png</img>
</div>
<div id="captions">
<div>foo</div>
<div>bar</div>
</div>

Select DIV tag with XSLT?

I'm trying to create a new XHTML document from another XHTML document. I only want to use some of the DIV tags in the old XHTML document, but I'm not sure if I'm doing it right. To start, if I want to select a special DIV tag with ID = mbContent, could I use
<xsl:template match="x:div[#id='mbContent']">
This DIV tag contains other DIVs and content like images and so on. How do I do if I want to use the same CSS style that is applied to the content? Is there a way to copy the CSS style or do I have to add new CSS style and how do I do that? Since the new XHTML document is going to be part of antother XHTML, I cant use HEAD tag and put a reference to the CSS stylesheet that way.
Hmm, but if I use the CSS stylesheet that is going to be in the HEAD of the main XHTML documnet, perhaps I could apply that CSS styles to this DIV, or? How do I apply styles in the new XHTML document?
I'm a little bit confused, but I hope my question isn't to confusing?! :)
Hi! I need some new help since the code below isn't working for me. It's this that isn't working
xmlns:x="http://www.w3.org/1999/xhtml" and "x:div[#id='mbContent']"
I think it's because I'm using a CMS tool that has a proxy module that not accept this code for some strange reason. Therefore I'm looking for some alternative solution to add CLASS or ID and also add values to DIV elements by using this instead xsl:apply-templates select="//*[#id='mbSubMenu']" and also use copy as in the example below? Preciate some new help! Thanks! :)
The Xpath used in the expression is fine until you are using 'x' as xmlns in the XSLT document.
The template will match for the <div> provided its id is mbContent and the selected context will have all the descendents.
You can change the inline CSS for the elements. Since you said that this part is going to be within some other XHTML document. You can choose XML as output.
Change the inline CSS if you want to.
You can also assign them different classes so that it takes global styles automatically.
The idea is that given a XML document you are transforming it to another XML document.
Therefore, you can apply styles as you like it.
I hope that answers your question.
P.S. use proper xmlns in the XPath expression.
Let's assume following is the HTML doc.
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title></title>
</head>
<body>
<div id="mbContent">
<div>
<span>Some complex structure</span>
</div>
</div>
</body>
Apply the following XSL
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:x="http://www.w3.org/1999/xhtml"
exclude-result-prefixes="x">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="x:div[#id='mbContent']">
<xsl:copy>
<xsl:attribute name="class">
<xsl:text>someNewStyle</xsl:text>
</xsl:attribute>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
This will result in the following output.
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title/>
</head>
<body>
<div class="someNewStyle" id="mbContent">
<div>
<span>Some complex structure</span>
</div>
</div>
</body>
You can change the XSL to suit your need.
Regards,
Ravish.