XSLT: How do I trigger a template when there is no input file? - xslt

I'm creating a template which produces output based on a single string, passed via parameter, and does not use an input XML document. xsltproc seems to happily run with a single parameter specifying the stylesheet, but I don't see a way to trigger a template without an input file (no parameter to xsltproc to run a named template, for example).
I'd like to be able to run:
xsltproc --stringparam bar baz foo.xsl
But I'm currently having to run, with the "main" template matching "/":
echo '<xml/>' | xsltproc --stringparam bar baz foo.xsl -
How can I get this to work? I'm sure I've seen other templates in the past which were meant to be run without an input document, but I don't remember how they worked or where to find them again. :-)

Actually, this has been done quite often.
In XSLT 2.0 it is defined in the Spec. that providing an initial context node is optional.
If no initial context node is provided (no source XML document), then it is important to provide the name of a named template which is to be executed as the entry point to the transformation.
In XSLT 1.0 one can provide to the transformation its own primary stylesheet module (file) as the source XML document, and of course, the transformation can completely ignore this source XML document. This technique has long ago been demonstrated and used by Jeni Tennison.
For example:
<?xml-stylesheet type="text/xsl" href="example.xml"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<p>Hello, world!</p>
</xsl:template>
</xsl:stylesheet>
When the above code is saved in a file named "example.xml" and then the folder contents is displayed with Windows Explorer, double-clicking on the file "example.xml" will open IE and produce:
Hello, world!

In general, you cannot do this with XSLT - specification requires there to be an input document, and for the processing to start with applying any available templates to its root node. Some XSLT processors might give a way to do what you want (e.g. execute a named template) as an extension, but I don't know any such, and it doesn't seem that xsltproc is one of them, judging from its man page.
In fact, this sounds pretty dubious in general, as the purpose of using XSLT to produce some output from a plain string input is unclear - it's not the kind of task it's generally good at.

Related

Generate Schematron "see" attribute value

I would like to dynamically generate the Schematron see attribute based on the the user home's directory. I could not get this working. Do you have an idea if this is possible? It needs to work in Oxygen XML. I am not sure if this is technically not possible in Schematron, or if this is a bug in Oxygen XML.
<?xml version="1.0" encoding="UTF-8"?>
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2"
xmlns:sqf="http://www.schematron-quickfix.com/validator/process">
<sch:pattern>
<sch:rule context="/">
<sch:let name="x" value="if (contains(base-uri(), 'myname'))
then 'http://www.a.com'
else 'http://www.b.com'"/>
<sch:report test="'a' = 'a'">
Hello world: "<sch:value-of select="$x"/>"
</sch:report>
</sch:rule>
</sch:pattern>
</sch:schema>
My goal is to generate a user-specific link to a locally deployed style guide, but, as you can see in the screenshot, the variable x is not resolved.
Maybe you can try to read the “user.home” system property: https://www.saxonica.com/html/documentation12/functions/fn/system-property.html
Using Schxslt it seems the syntax of an XSLT attribute value template in the form of e.g. see="{$x}" in Schematron then generates an SVRL report having the URI in the form of e.g.
<svrl:successful-report location="/" see="http://www.b.com" test="'a' = 'a'">
<svrl:text>
Hello world
</svrl:text>
</svrl:successful-report>
I don't know whether oXygen has any integration for Schxslt as a Schematron validator and for rendering the validation result in the form of SVRL.
To build on the answer by #radu-coravu, you might be able to get the 'user.home' value by using a function in an external XSLT file:
Use xsl:include in your Schematron file to include the XSLT. See, e.g., https://github.com/AntennaHouse/focheck/blob/43bd5d491d8b985395c94c3c53083770d8c461b6/schematron/fo-property.sch#L23
Write a function in the external XSLT that returns the "user.home" system property value
In your rule in your Schematron, use let to get the return value of the function as a variable value. See, e.g., https://github.com/AntennaHouse/focheck/blob/43bd5d491d8b985395c94c3c53083770d8c461b6/schematron/fo-property.sch#L31
Use the Schematron variable in your assert and report just like any other variable

Processing the output of another XSLT Stylesheet

I have an XSLT stylesheet that produces some output in XML. I want to processes that output with another stylesheet. Is there a way to tell the latter stylesheet to "run and use" the results from the former?
There is not, as far as I know, a standard way to tell an XSLT processor to run another stylesheet on given input and do something with the output. In some cases you can process the input against one set of templates and save the result in a variable, then apply a different set of templates to the value of the variable, something like this:
<xsl:template match="/">
<xsl:variable name="temp">
<xsl:apply-templates mode="first-pass"/>
</xsl:variable>
<xsl:apply-templates select="$temp" mode="second-pass"/>
</xsl:template>
This assumes you're running XSLT 2.0. In XSLT 1.0 you will need a processor that supports the node-set extension (many do), and you'll need to change the reference to $temp to something like exslt:nodeset($temp).
As you will perceive, this won't work very well if your two stylesheets both use the default mode and operate on overlapping sets of element types. So some XSLT processors have added extensions to provide the kind of functionality you describe (see, for example, discussions of the Xalan pipe:pipeDocument extension element).
Of course, you can also handle the pipe outside of XSLT. The simplest way to do it depends upon the environment you are running in.
If you're running XSLT from an operating system shell and your XSLT processor accepts input on stdin, you can pipe the output from one stylesheet into the other:
xsltproc a.xsl in.xml | xsltproc b.xsl - > out.xml
And as mohammed moh has already pointed out, many scripting environments make it possible to do similar things: he mentions PHP, and of course there's XProc.
yes You can. You must Transforms the source node to a DOMDocument I don't Know What is your Programming Language . For Example in php is transformToDoc() after Transforms You Can Run A New XSLT Stylesheet On DOMDocument Output

Transforming one XML document into another with C++

What would be a straightforward way to transform a source XML document into a destination XML document. There are only small differences between source and destination: Specifically I want to delete the first UnitIDRecord-Node within each UnitIDGroup-Node.
What would be the appropriate model for this task DOM or SAX?
What XML-library would best fit this problem (which guarantees that the source and destination only differs in the deleted nodes, no missing namespace, attributes, encoding, ...)?
I read about XSLT, could this be an option?
The XML document looks like following:
<?xml version="1.0" encoding="UTF-8"?>
<ExPostInformationRealGeneration xmlns="http://schemas.seven2one.de/EEX/TransparencyPlatform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schemas.seven2one.de/EEX/TransparencyPlatform EEXTransparencyPlatform.xsd">
<DispatcherID>XYZ</DispatcherID>
<CreationDateTime>2012-05-22T13:57:00Z</CreationDateTime>
<MessageText>1 - Positiv - Meldung mit Quality-Tag - L000</MessageText>
<UnitIDGroup>
<UnitID>E110200-001</UnitID>
<UnitIDRecord><Quantity>16.9</Quantity><Starttime>2008-04-30T22:00:00Z</Starttime><Period>PT1H</Period><MessageText></MessageText></UnitIDRecord>
<UnitIDRecord><Quantity>16.6</Quantity><Starttime>2008-04-30T23:00:00Z</Starttime><Period>PT1H</Period><MessageText></MessageText></UnitIDRecord>
<UnitIDRecord><Quantity>16.4</Quantity><Starttime>2008-05-01T00:00:00Z</Starttime><Period>PT1H</Period><MessageText></MessageText></UnitIDRecord>
</UnitIDGroup>
<UnitIDGroup>
<UnitID>E110200-002</UnitID>
<UnitIDRecord><Quantity>16.9</Quantity><Starttime>2008-04-30T22:00:00Z</Starttime><Period>PT1H</Period><MessageText></MessageText></UnitIDRecord>
<UnitIDRecord><Quantity>16.6</Quantity><Starttime>2008-04-30T23:00:00Z</Starttime><Period>PT1H</Period><MessageText></MessageText></UnitIDRecord>
<UnitIDRecord><Quantity>16.4</Quantity><Starttime>2008-05-01T00:00:00Z</Starttime><Period>PT1H</Period><MessageText></MessageText></UnitIDRecord>
</UnitIDGroup>
<UnitIDGroup>
<UnitID>E110201-001</UnitID>
<UnitIDRecord><Quantity>7.0</Quantity><Starttime>2008-04-30T22:00:00Z</Starttime><Period>PT1H</Period><MessageText></MessageText></UnitIDRecord>
<UnitIDRecord><Quantity>7.1</Quantity><Starttime>2008-04-30T23:00:00Z</Starttime><Period>PT1H</Period><MessageText></MessageText></UnitIDRecord>
<UnitIDRecord><Quantity>7.1</Quantity><Starttime>2008-05-01T00:00:00Z</Starttime><Period>PT1H</Period><MessageText></MessageText></UnitIDRecord>
</UnitIDGroup>
<!-- other UnitIDGroup elements -->
</ExPostInformationRealGeneration>
I would consider the possibility of reading the file in as strings and writing the string out to another file if it matches your criteria. That's a 5 line program and avoids any parsing etc. It will run quickly and is simple. But, it is specific to this problem and not reusable. I offer this therefore as a suggestion not the correct solution!

preproccesing in XSLT

is it at all possible to 'pre-proccess' in XSLT?
with preprocessing i mean updating the (in memory representation) of the source tree.
is this possible, or do i need to do multiple transforms for it.
use case:
we have Docbook reference manuals for out clients but for certain clients these need different 'skins' (different images etc). so what i was hoping to do is transform the image fileref path depending on a parameter. then apply the rest of the normal Docbook XSL templates.
Expanding on Eamon's answer...
In the case of either XSLT 1.0 or 2.0, you'd start by putting the intermediate (pre-processed) result in an <xsl:variable> element, declared either globally (top-level) or locally (inside a template).
<xsl:variable name="intermediate-result">
<!-- code to create pre-processed result, e.g.: -->
<xsl:apply-templates mode="pre-process"/>
</xsl:variable>
In XSLT 2.0, the value of the $intermediate-result variable is a node sequence consisting of one document node (was called "root node" in XSLT/XPath 1.0). You can access and use it just as you would any other variable, e.g., select="$intermediate-result/doc"
But in XSLT 1.0, the value of the $intermediate-result variable is not a first-class node-set. Instead, it's something called a "result tree fragment". It behaves like a node-set containing one root node, but you're restricted in how you can use it. You can copy it and get its string-value, but you can't drill down using XPath, as in select="$intermediate-result/doc". To do that, you must first convert it to a first-class node-set using your processor's node-set() extension function. In Saxon 6.5, libxslt, and 4xslt, you can use exsl:node-set() (as in Eamon's answer). In MSXML, you'd need to use msxsl:node-set(), where xmlns:msxsl="urn:schemas-microsoft-com:xslt", and in Xalan, I believe it's called xalan:nodeset() (without the hyphen, but you'll have to Google for the namespace URI). For example: select="exsl:node-set($intermediate-result)/doc"
XSLT 2.0 simply abolished the result tree fragment, making node-set() unnecessary.
This is not possible with standards compliant XSLT 1.0. It is possible in every actual implementation I've used, however. The extensions with which to do that differ by engine, however. It is also possible in standard XSLT 2.0 (which is in any case much easier to work with - so if you can, just use that).
If your xslt processor supports EXSLT, the exsl:node-set() function does what you're looking for. msxml has an identically named extension function as well (but with a different namespace uri, the functions are unfortunately not trivially compatible).
Since you are trying to generate slightly different output from the same DocBook XML source, you might want to look into the "profiling" (conditional markup) support in DocBook XSL stylesheets. See Chapter 26 in DocBook XSL: The Complete Guide by Bob Stayton:
Profiling is the term used in DocBook
to describe conditional text.
Conditional text means you can create
a single XML document with some
elements marked as conditional. When
you process such a document, you can
specify which conditions apply for
that version of output, and the
stylesheet will include or exclude the
marked text to satisfy the conditions.
This feature is useful when you need
to produce more than one version of a
document, and the versions differ in
minor ways.
For example, to use different images for, say, Windows and Mac versions of the same document, you might have a DocBook XML fragment like this:
<figure>
<title>The Foo dialog</title>
<mediaobject>
<imageobject os="windows">
<imagedata fileref="screenshots/windows/foo.png"/>
</imageobject>
<imageobject os="mac">
<imagedata fileref="screenshots/mac/foo.png"/>
</imageobject>
</mediaobject>
</figure>
Then, you would use the profiling-enabled versions of the DocBook XSL stylesheets with the profile.os parameter set to windows or mac.
Maybe you should use XSLT "OOP" methods here. Put all the common templates to all clients in a stylesheet, and create an stylesheet for each client with specific templates overriding common ones. Import the common stylesheet within the specific ones with xsl:import, and you'll do only one processing by calling the stylesheet corresponding to a client.

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/>