DocBook XSL - ok to edit the original stylesheets? - xslt

I would like to make certain lines of a DocBook table of contents bold based on whether the particular <section> has a certain attribute defined. This is easy by adding an <xsl:if test="..."> statement to the DocBook XSL (fo/autotoc.xsl lines 187-230 -- this is for output to PDF using XMLMind).
I'm wondering, though, if it's bad practice to edit the DocBook XSLs themselves. I have other customizations in a separate XSL of my own, mostly setting parameters, but I can't imagine how I would introduce this conditional logic--based on which line of the TOC is currently being processed--without putting some sort of code in the originals. Any thoughts? How do you upgrade to a newer DocBook XSL after making changes?

Import the docbook stylesheets from your own XSLT. Then, (re)define the Docbook template that you want to "override".
Since your template will be the highest in the import tree, it will take precedence.
By doing it that way you don't have to modify any of the core docbook XSLT files. It will make upgrades of the Docbook stylesheets easier in the future.
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="xsl/fo/docbook.xsl"/>
<xsl:template match="template-that-you-need-to-redefine">
...
</xsl:template>
</xsl:stylesheet>

Related

How to preserve processing instructions whilst producing XSL-FO in eXist-db?

Is there a way to make eXist to return processing instruction as it is? It seems it somehow ignores it in the output.
Processing instructions are very useful if I use XEP as my rendering engine, hence it would be great to be able to preserve them before the root of the XSL-FO document or immediately after its start.
If I have in the template:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:tei="http://www.tei-c.org/ns/1.0" version="2.0">
<xsl:template match="/">
<fo:root>
<?xep-pdf-page-layout two-columns-right?>
It simply returns:
<fo:root xmlns:tei="http://www.tei-c.org/ns/1.0" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
I’ve tried to touch preserving of processing instructions with:
declare option exist:serialize "method=xhtml media-type=text/xml process-xsl-pi=yes"; in the prolog of my query but to no avail.
UPDATE I
The steps of my scenario are:
loading the document from the DB
passing it to the transform:transform() function
whilst calling the transform:transform() function, I pass to it one basic stylesheet, which includes other stylesheets
I need to add the processing instruction to a stylesheet for pages-masters, which covers the root of the FO document later used by the rendering engine. This stylesheet is included (<xsl:include/>) into the basic one (gathering all the stylesheets) and passed to the function.
With XSLT, to output a processing instruction use https://www.w3.org/TR/xslt/#creating-processing-instructions, that is <xsl:processing-instruction name="xep-pdf-page-layout">two-columns-right</xsl:processing-instruction> to have the pi <?xep-pdf-page-layout two-columns-right?> in the transformation result.

XInclude in XSLT Stylesheets (eXist-db)

Some of my stylesheets are a bit large and some of their parts are repeating. I would like to use XInclude for them—which would allow me to separate them aside the whole stylesheet. I can’t use xsl:import or xsl:include here because I need to inject them into the specific place for generating bookmarks and active links (for XSL-FO).
If I use:
<xi:include href="/db/apps/tested-bunny/resources/xsl-fo/common/bookmark-tree.xml/>
… the .fo file produced really includes the part. However, the part is untranslated, which means it is there as is in the source. The XSL-FO processor thus ignores it and the pdf result is without bookmarks.
As for the separated part—I saved it as a regular XML file with two namespaces declared in the root element:
<fo:bookmark-tree xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:if test="$head-level ge '1'">
...
If I try to include the same code snippet in a form of XSL stylesheet, it is the same—it is injected there properly but it does not add its functionality to the whole stylesheet, it is there still untranslated.
Is there any specific practice or limitation I am not aware of? How to do that properly?
For me, the working solution was not XInclude but xsl:include and calling the template at the proper time:
...
</fo:declarations>
<!-- Bookmarks from the external stylesheet -->
<xsl:call-template name="bookmark-tree"/>
<fo:page-sequence master-reference="title-page">
...
I created the proper stylesheet. The important thing was to set the root element to the current context:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" version="2.0">
<xsl:template name="bookmark-tree" match=".">
<fo:bookmark-tree>
...
And of course, it was necessary to include the stylesheet into the one where I call the template:
<xsl:include href="common/bookmark-tree.xsl"/>
For now, I consider this question as answered.

XSLT Getting two different output for same input XML for same XSL

I have been trying my XSLT code in the online tool [XSLT 1.0 processor]:
http://www.freeformatter.com/xsl-transformer.html
Recently, I had to make use of xs:dateTime and hence started using the tool that uses XSLT 2.0 processor,
http://xsltransform.net/
Now, when i was trying to solve a problem, i see that i get different output for the same input XML in these two processors. The code posted here is not the real code i am working on; this is to simulate the weird output i faced.
XML:
<?xml version="1.0" encoding="UTF-8"?>
<items>
<book>
<title></title>
</book>
<phone>apple</phone>
</items>
XSLT:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8"
indent="yes" />
<xsl:template match="/">
<items><xsl:apply-templates select="items/*" /></items>
</xsl:template>
<!-- ignore empty elements -->
<xsl:template match="*[not(normalize-space())]" />
<xsl:template match="book">
<newbook><xsl:apply-templates /></newbook>
</xsl:template>
<xsl:template match="title">
<newtitle><xsl:apply-templates /></newtitle>
</xsl:template>
<xsl:template match="phone">
<newphone><xsl:apply-templates /></newphone>
</xsl:template>
</xsl:stylesheet>
Output from http://xsltransform.net/ : [output 1]
<?xml version="1.0" encoding="UTF-8"?>
<items>
<newbook>
<newtitle/>
</newbook>
<newphone>apple</newphone>
</items>
Output from http://www.freeformatter.com/xsl-transformer.html : [output 2]
<?xml version="1.0" encoding="UTF-8"?>
<items>
<newphone>apple</newphone>
</items>
Expected output XML is output 2 .
Any idea why this different output behavior?
note: I looked at another SO question: Generating two different outputs for the same XSL file?, but that is different.
Edit
To add more clarity on which tool produced which output:
http://xsltransform.net/ produced output 1
http://www.freeformatter.com/xsl-transformer.html produced output 2
Update[02/10/2014] - regarding solution
Since the correct answer was first provided by Jim Garrison, that's marked as answer. However, there are other important points as well, as pointed out by others, Hence I am wrapping up all here.
<xsl:template match="*[not(normalize-space())]" />
The above template eliminates the empty nodes in both XSLT1 and XSLT2. Hence output 2 is correct
*Reason for getting output 1 from the tool - http://xsltransform.net/ :*
As #michael.hor257k pointed out, there are two templates that matches the <book> element.
The tool was using Saxon-HE 9.5.1.3, and the output 1 behavior was probably a bug that was fixed in the latest maintenance release [#Michael Kay explained well in his answer]
As #Ian Roberts mentioned in his answer, there is a concept of default priorities that are assigned to different types of templates.
Changing the template syntax to:
<xsl:template match="*[not(normalize-space())]" priority="2"/>
yields the same output in both the tools regardless of Saxon version, because we are explicitly defining the priority to the template.
Output 2 is correct. I ran your example in Oxygen/XML, using both XSLT1 and XSLT2 and got the correct results for both. Your template
<xsl:template match="*[not(normalize-space())]" />
eliminates the empty nodes in both XSLT1 and XSLT2.
Therefore the only possible conclusion is that the online tool that produced your Output 1 has a bug.
I get the correct result running with Saxon-EE 9.5.1.4; the xsltransform site is running Saxon-HE 9.5.1.3. It's probably a bug that we fixed in the latest maintenance release; I'll do some chasing to see if I can finger it.
Note that an empty <book> element matches two of your templates:
<xsl:template match="*[not(normalize-space())]" />
and:
<xsl:template match="book">
<newbook><xsl:apply-templates /></newbook>
</xsl:template>
The same thing applies to an empty <title> element. Such conflicts must be resolved by the XSLT processor, using the priorities specified in the XSLT specification.
The result you report from http://xsltransform.net/ is generated by Saxon 9.5.1 - an XSLT 2.0 engine. You will get a different result (on the same site) if you switch to an XSLT 1.0 engine (Saxon 6.5 or Xalan 2.7.1).
I don't know if the conflict resolution rules have changed in XSLT 2.0 compared to XSLT 1.0; I am getting a different result myself when using Saxon 8.9 - also an XSLT 2.0 processor. Perhaps we will be fortunate enough to hear from Michael Kay regarding this difference (I have added a Saxon tag to your question to get his attention)..
Output 2 is definitely the correct one according to both the 1.0 and 2.0 XSLT specs for the stylesheet you give in the question. However you say this is simplified when compared to your real stylesheet.
The thing that makes it work is the default priorities that are assigned to different types of templates. These are the same in both XSLT versions, and a match pattern that involves a predicate will overrule one that is just a single element name test.
However a match that involves hierarchy (e.g. match="items/book") or anything more complex than just a single element name will get the same priority as the *[....] rule by default - 0.5. If you've got any of these kinds of patterns in your real XSLT then you need to add an explicit priority higher than 0.5 to the "blank-suppressor" template.
<xsl:template match="*[not(normalize-space())]" priority="2"/>

declare global variable in project and use it in xslt

I would seek this your guidance for doubt in XSLT. In my current project there is a requirement to create many XSLT files. In these transformations, there are few common steps performed; for.eg. changing uppercase of an element value from input xml. I'm currently using the below code in an XSLT, so if there are 50 XSLT being created then this code will be duplicated.
<xsl:variable name="smallcase" select="'abcdefghijklmnopqrstuvwxyz'" />
<xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
<xsl:message>UPPERCASE is <xsl:value-of select="translate($MsgType, $smallcase, $uppercase)" /></xsl:message>
Requesting your advice on how to avoid code duplication. Can I create a common XML file such as utility and declare the variables uppercase and smallcase and shall I call these variables inside the xslt. Similar to other prog. lang like java where I can declare a common function globally and use it in different classes. Basically I would like to know whether it is possible to declare globally and use it in all the xslt.
I would use <include/> to include the XSLT file with all your global variables defined.
See also http://www.w3.org/TR/xslt#element-include
Put all your variables into the file "my_global_variables.xsl":
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:variable name="myVariable" select="'xyz'"/>
<!-- more variables to add -->
</xsl:stylesheet>
Your main stylesheet looks like this then, including the "my_global_variables.xsl":
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:include href="my_global_variables.xsl"/>
<xsl:template match="/">
</xsl:template>
</xsl:stylesheet>
There is also the <import> element with which you can import stylesheets. An imported style sheet has lower precedence than the importing style sheet though - so in your case I would use <include>.
Requesting your advice on how to avoid code duplication. Can I create
a common XML file such as utility and declare the variables uppercase
and smallcase and shall I call these variables inside the xslt.
<xsl:import> and <xsl:include> are the two XSLT instructions especially designed for this task.
Global variables (children of an xsl:stylesheet element) in a stylesheet module are accessible in the stylesheet that includes this stylesheet. The rules with importing are a little bit more complicating, but if there are no naming conflicts between global variables from imported stylesheets, they are all accessible from the importing stylesheet.
Finally, I recommend not to use www.w3schools.com -- see why at: http://www.w3fools.com

Merge multiple xslt stylesheets

I have a xslt stylesheet with multiple xsl:imports and I want to merge them all into the one xslt file.
It is a limitation of the system we are using where it passes around the xsl stylesheet as a string object stored in memory. This is transmitted to remote machine where it performs the transformation. Since it is not being loaded from disk the href links are broken, so we need to remove the xsl:imports from the stylesheet.
Are there any tools out there which can do this?
You can use an XSL stylesheet to merge your stylesheets. However, this is equivalent to using the xsl:include element, not xsl:import (as Azat Razetdinov has already pointed out). You can read up on the difference here.
Therefore you should first replace the xsl:import's with xsl:include's, resolve any conflicts and test whether you still get the correct results. After that, you could use the following stylesheet to merge your existing stylesheets into one. Just apply it to your master stylesheet:
<?xml version="1.0" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="xsl:include">
<xsl:copy-of select="document(#href)/xsl:stylesheet/*"/>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The first template replaces all xsl:include's with the included stylesheets by using the document function, which reads in the file referenced in the href attribute. The second template is the identity transformation.
I've tested it with Xalan and it seems to work fine.
It is impossible to include imported stylsheets into the main file without breaking import precedence. For example, you define a top-level variable in an imported stylesheet and redefine it in the main file. If you merge two files into one, you’ll get two variables with the same name and import precedence, which will result in an error.
The workaround is two replace xsl:import’s with xsl:include’s and resolve any conflicts. After that you are safe to replace xsl:include instructions with the corresponding files’ contents, because that is what XSLT-processor does:
The inclusion works at the XML tree level. The resource located by the href attribute value is parsed as an XML document, and the children of the xsl:stylesheet element in this document replace the xsl:include element in the including document. The fact that template rules or definitions are included does not affect the way they are processed.
A Manual merge is probably going to be the best option.
The main consideration will probably be to make sure that the logic for matching templates works in the combined stylesheet.
Why would you want to? They're usually seperated for a reason afterall (often maintainability)
You could always write the merge yourself - read the XSL files in, select the template items you're interested in and write to a new master XSL file...
import multiple xsl in single xsl
<xsl:import href="FpML_FXOption_Trade_Template1.xsl"/>
<xsl:apply-imports/>
<calypso:keyword>
<calypso:name>DisplayOptionStyle</calypso:name>
<calypso:value>Vanilla</calypso:value>
</calypso:keyword>
<xsl:import href="FpML_FXOption_Trade_Template2.xsl"/>
<xsl:apply-imports/>