XML to XHTML transformation - html

I need to transform a XML to XHTML. Within the XML are multiple paragraphs and embedded quotations e.g.
<para>SomeText</para>
<para><quote>SomeText</quote></para>
<para>SomeText</para>
I tried this:
<xsl:choose>
<xsl:when test="//text/para">
<xsl:for-each select="//text">
<xsl:for-each select="//para">
<p><xsl:value-of select="text()"/></p>
</xsl:for-each>
</xsl:for-each>
</xsl:when>
<xsl:when test="//text/para[quote]">
<xsl:for-each select="//text">
<xsl:for-each select="//para/quote">
<p><q><xsl:value-of select="text()"/></q></p>
</xsl:for-each>
</xsl:for-each>
</xsl:when>
</xsl:choose>
The second condition simply gets ignored however.

As #LarsH indicates, avoid <xsl:for-each>. Use template matching.
This simple transformation:
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="para">
<p><xsl:apply-templates /></p>
</xsl:template>
<xsl:template match="quote">
<q><xsl:apply-templates /></q>
</xsl:template>
</xsl:transform>
will turn this:
<text>
<para>SomeText</para>
<para><quote>SomeText</quote></para>
<para>SomeText</para>
</text>
into
<p>SomeText</p>
<p><q>SomeText</q></p>
<p>SomeText</p>
Further reading here on SO:
What are the differences between 'call-template' and 'apply-templates' in XSL?
Why does XSLT output all text by default?
What is the default select of XSLT apply-templates?

The problem is you're using XPath expressions that ignore context (aside from the context document), i.e. expressions that start with //. So if you have any <para> element anywhere in the document that has a <quote> child element, the first condition will always be true and the second condition will never be reached.
Really you want to move the for-each loop (or probably better, a set of templates with match patterns and apply-templates) outside of the choose/when conditions. In fact you will probably not need a choose/when at all, once you have the right templates.
(Moving in some info from comments)
XSLT and XPath are sufficiently complex that it's really worth learning the basics before trying to get something working by trial-and-error. You'll save a lot of time that way. For a general introduction I would recommend
How XSLT Works
Then for more advanced discussion of xsl:for-each vs. xsl:apply-templates, see
Jeni Tennison's blog post Matching templates, named templates or for-each? My rules of thumb
differences between for-each and templates in xsl?

Related

XSLT: What is the best way to apply a floating text that will display the full expression for acronyms from an external dictionary file?

I'm new to XSLT.
I need to apply floating text that displays a setting for acronyms, as soon as the mouse cursor hovers over a word that is abbreviated.
My input file is XML, and every word that should display acronyms is represented in the following format:
<abbreviation Id="E.G."/>
When the mouse cursor hovers over the word EG, the floating text will be displayed: "for example".
I thought to apply the floating text using the HTML abbr tag
And the code I wrote is:
<xsl:template match="abbreviation">
<abbr title="for example.">
<xsl:value-of select="#Id"/>
</abbr>
</xsl:template>
I want the "title" attribute to be given as a parameter the definition of the main boxes from an external file that will contain a dictionary of related abbreviations and explanations.
I would love to know how to apply my issue.
And also get ideas about the external dictionary file - what kind of file should you create? For example, I would like a good structure of the file.
Note:
I use OXYGEN EDITOR,
And I believe I can also get solutions in XSLT version 2 and 3
Suppose your external dictionary looks like:
dict.xml
<dictionary>
<entry abbr="C.V.">course of life</entry>
<entry abbr="E.G.">for example</entry>
<entry abbr="N.B.">note well</entry>
</dictionary>
You can then use a key to transform an input like:
XML
<root>
<abbreviation Id="E.G."/>
</root>
using:
XSLT 2.0 (untested)
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="path-to-dictionary" select="'dict.xml'"/>
<xsl:key name="abbr-lookup" match="entry" use="#abbr" />
<xsl:template match="/root">
<html>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="abbreviation">
<abbr title="{key('abbr-lookup', #Id, document($path-to-dictionary))}">
<xsl:value-of select="#Id"/>
</abbr>
</xsl:template>
</xsl:stylesheet>
to get:
Result
<html>
<body>
<abbr title="for example">E.G.</abbr>
</body>
</html>
To understand the syntax used, read about Attribute Value Templates.
Well, XSLT does not float any text, it is a programming language to transform XML (or with XSLT 2 or 3, other input formats) to XML, (X)HTML, plain text. You seem to want to transform your XML to HTML in the hope the HTML user agent or browser displays a tooltip of the title attribute.
As for using an XML file as a secondary input file, if you have abbrvs.xml with
<root><abbr key="E.G.">for example.</abbr>...</root>
then in XSLT you can use
<abbr title="{key('abbr', #Id, doc('abbrvs.xml'))}">
<xsl:value-of select="#id"/>
</abbr>
to pull that title attribute value from the secondary input file if the XSLT declares a key
<xsl:key name="abbr" match="abbr" use="#key"/>

XSL: How to use HTML tags inside stylesheet?

I got this code in my XSL stylesheet:
<xsl:for-each select="report:column-names/report:column">
<fo:table-cell display-align="center" font-size="9pt">
<fo:block font-family="{$font.family}" font-weight="bold">
<xsl:value-of select="." disable-output-escaping="yes" /> <--problematic value
</fo:block>
</fo:table-cell>
</xsl:for-each>
In the problematic value that I mentioned, I got values that I want to wrap with bdi html tag.
I tried to just put bdi and I didn't see my value, like this:
<bdi><xsl:value-of select="." disable-output-escaping="yes" /></bdi>
How can apply this tag for my values?
You cannot mix HTML and XSL-FO. XSL-FO is an XML vocabulary that is defined for formatting. The original purpose of XSLT was to transform arbitrary XML vocabularies (the 'X' in 'XML' comes from 'Extensible', after all) into the standard formatting vocabulary. That's what you've been doing with the XSLT in your question.
The description of Unicode BIDI Processing in XSL 1.1 is at https://www.w3.org/TR/xsl11/#d0e4879. The applicable FO is fo:bidi-override (https://www.w3.org/TR/xsl11/#fo_bidi-override), and the applicable properties are direction (https://www.w3.org/TR/xsl11/#direction) and unicode-bidi (https://www.w3.org/TR/xsl11/#unicode-bidi).
You so far haven't shown the content of a report:column element, but it looks like you want:
<fo:bidi-override unicode-bidi="embed" direction="rtl">
<xsl:value-of select="." disable-output-escaping="yes" />
</fo:bidi-override>
Without seeing a report:column element, the embed and rtl are just guesses.
(Using disable-output-escaping is seldom a good idea, but we can't see how bad an idea it is in this case without seeing a report:column element that needs it.)

xslt: Use variables in select and match

I need to specify the output order from a html file to a text file. Therefore I use the xsl:apply-templates select approach.
It works ok but in order to fine tune the output of the different nodes I need a corresponding template, not just a general one. This also works ok but I need to repeat the select pattern in the match pattern for the template.
I like to define a variable that holds the pattern so it only needs to be defined once.
Below is my simplified style sheet and simplified html which does not work but gives an idea of what I want to accomplish.
Is it possible to use variables like this? I can use both xslt 1.0 and 2.0 if needed.
<xsl:stylesheet ...>
...
<xsl:variable name="first">div[#class='one']</xsl:variable>
<xsl:variable name="second">div[#class='two']</xsl:variable>
<xsl:template match="/*">
<xsl:apply-templates select="//$first"/>
<xsl:apply-templates select="//$second"/>
...
</xsl:template>
<xsl:template match="//$first">
<xsl:text>Custom text for class one:</xsl:text><xsl:value-of select="text()"/>
</xsl:template>
<xsl:template match="//$second">
<xsl:text>Custom text for class two:</xsl:text><xsl:value-of select="text()"/>
</xsl:template>
</xsl:stylesheet>
The html:
...
<div class="two">text from two</div>
<div class="one">text from one </div>
...
Desired output:
Custom text for class one: text from one
Custom text for class two: text from two
There is no way to use variables like that in XSLT 1 or 2. The only way would be to write a stylesheet producing a second stylesheet and execute that separately.
In XSLT 3 there are new features called static variables/parameters and shadow attributes that could help or there you could use the transform function to execute a newly generated stylesheet directly with XSLT instead of in a separate step with a host language.
But using XSLT 2 you can shorten the
<xsl:apply-templates select="//div[#class='one']"/>
<xsl:apply-templates select="//div[#class='two']"/>
to
<xsl:apply-templates select="//div[#class='one'], //div[#class='two']"/>
For completeness here is the XSLT 3 approach with two static parameters used in shadow attributes:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
exclude-result-prefixes="xs math"
version="3.0">
<xsl:param name="first" static="yes" as="xs:string" select=""div[#class='one']""/>
<xsl:param name="second" static="yes" as="xs:string" select=""div[#class='two']""/>
<xsl:template match="/*">
<xsl:apply-templates _select="//{$first}, //{$second}"/>
</xsl:template>
<xsl:template _match="{$first}">
<xsl:text>Custom text for class one:</xsl:text><xsl:value-of select="text()"/>
</xsl:template>
<xsl:template _match="{$second}">
<xsl:text>Custom text for class two:</xsl:text><xsl:value-of select="text()"/>
</xsl:template>
</xsl:stylesheet>
Variables in XSLT hold values, not fragments of expressions. (In other words, XSLT is not a macro language).
As an alternative to Martin's solution, which requires XSLT 3.0, you could consider using what are sometimes called "meta-stylesheets" - do a transformation as a pre-processing step on the stylesheet itself. You could even write the generic stylesheet to use the XSLT 3.0 syntax with shadow attributes like _match, and do an XSLT preprocessing phase to convert this to regular XSLT 1.0 or 2.0 syntax for execution.

XSLT v1.0 - document() call external template

I am trying to access a xsl template in a different xsl file from inside my template. The 2 templates are on different XSL files and that's the reason I need the document function. The templates also apply on different xml files.
My problem is how to call the second template from inside the first one. Sample of my code:
I am inside template Library:
<xsl:template match="Library">
<fo:table table-layout="fixed" width="160mm">
<fo:table-column column-width="80mm"/>
<fo:table-column column-width="80mm"/>
<fo:table-body>
<fo:table-row>
<xsl:for-each select="document(Library/#File)/Document/Books">
<xsl:apply-templates select="."/>
</xsl:for-each>
</fo:table-row>
<fo:table-body>
</fo:table>
</xsl:template>
and I am trying to access the template Books:
<xsl:template match="Books">
<fo:table-cell>
<fo:block font-family="arial" font-size="8pt" text-align="left">
<xsl:value-of select="substring(#IBAN,4)"/>
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block font-family="arial" font-size="8pt" text-align="left">
<xsl:value-of select="#date"/>
</fo:block>
</fo:table-cell>
</xsl:template>
The first argument inside the document function does not seem to be set correctly. Any thoughts on how should i rewrite it ? I couldn't find any issue in the forum having a similar problem.
Any help would be appreciated, thanks
To bring a template in from another XSLT file, use xsl:import or xsl:include. Use xsl:document when you want to apply XSLT templates to the XML found in the specified document in addition to the default input XML.
Unfortunately, you will not be able to use a variable in the path to the XSLT file because the path is resolved at compile-time.
If the path cannot be determined statically (possibly achieving the needed variation via a relative path specification), you might want to reconsider your overall organization. You could go so far as to compose your XSLT programatically before running it so as to be able to dynamically write the static #href, but do reconsider your overall solution architecture before going that far.

XSL Processing Order

I've had trouble finding an exact, and simple, answer to this question on SO or elsewhere:
In XSL files, how can you tell which template will be processed first, second, etc? I read that it was ordered by how specific the XPath was. Additionally, is there a difference in XSL 1.0 vs. 2.0?
Finally, here is a flawed XSL file I am toying with. Currently the output is just the title "Table of Contents". I'll attach the XML here as well.
<xsl:template match="/">
<h1>
<xsl:text>Table of Contents</xsl:text>
</h1>
</xsl:template>
<xsl:template match="heading1">
<h2>
<xsl:value-of select="."/>
</h2>
</xsl:template>
<p>
<xsl:text>This document contains </xsl:text>
<xsl:value-of select="count(/article/body/heading1)"/>
<xsl:text> chapters. </xsl:text>
</p>
and the XML:
<article>
<title>
Creating output
</title>
<body>
<heading1>Generating text</heading1>
<heading1>Numbering things</heading1>
<heading1>Formatting numbers</heading1>
<heading1>Copying nodes from the input document to the output</heading1>
<heading1>Handling whitespace</heading1>
</body>
Any explanation as to why all the content isn't being displayed? Thank you for your help!
Here's what's happening:
The XSLT processor reads the root element of your XML
Then it looks in the stylesheet to see what matches. It finds your first template
It executes the first template.
The first template says to output the text, and then does nothing else, so the XSLT processor moves on to the next input element.... but you've processed the entire root node, so there are no more input nodes at the same level. It's done.
What you need to do is put an <xsl:apply-templates/> inside the first template. When this is encountered by the processor, it starts over but this time the context is the list of second-level nodes inside the root. It will look at each XML node in turn, find the best matching template in your stylesheet, and execute it.
This is a key concept -- The template is NOT in control, and is not procedural.