I have a parameter which currently is read from a file:
<xsl:param name="source" select="document('filename.xml')" />
but now I need to replace it with a string containing xml that comes from external source, and I get an error
XPTY0019: Required item type of first operand of '/' is node(); supplied value has item type xs:string`
Unfortunately I cannot use saxon's exsl:node-set() function, for business reasons I have to use version that doesn't support it. Is it possible to get node() from string in some other way?
Firstly, the process of "converting a string to a node" is usually called parsing, and calling the process by its proper name might help to find the right solution.
You're in an xsl:param, which means you're on the boundary/interface between the XSLT processor and the outside world, and that presumably means you have the option of doing the parsing on either side of the boundary. If you aren't able to use the XSLT 3.0 function parse-xml() or a Saxon extension function that pre-dates it, then the best option seems to be to do the parsing in the calling application and supply the resulting node as the parameter value.
The exsl:node-set() function is defined to convert a "result tree fragment" (not a string) into a node-set. I believe some implementations of it will do parsing if supplied with a string rather than an RTF but that has never been true of the Saxon implementation. For many years Saxon has had an extension function saxon:parse() whose functionality is very close to fn:parse-xml().
Why not externalize the process for converting the string into XML? Create a proxy service that is used to fetch the external String and convert into an XML response. Then, you can request the XML from your proxy service:
<xsl:param name="source" select="document('http://localhost/myCustomProxyService?file=filename.xml')"/>
Related
I want to use saxon to transform xml
and in the XSLT ,
i add a parameter which i expect it as a "Document"
<xsl:param name="doc" as="node()*" />
and in java ,
DOM4JDocumentWrapper documentNode = new DOM4JDocumentWrapper(doc4j, "", config);
NodeInfo nodeInfo=documentNode.getRoot();
XsltTransformer tr = xsltExecutable.load();
tr.setParameter(idQname, new XdmNode(nodeInfo));
but met such error:
Caused by: net.sf.saxon.trans.XPathException: Cannot convert value class net.sf.saxon.option.dom4j.DOM4JNodeWrapper of type element(Q{}xxx) to class net.sf.saxon.tree.tiny.TinyElementImpl
at net.sf.saxon.expr.PJConverter$Identity.convert(PJConverter.java:527)
It looks to be as if the Saxon Configuration you are using is not aware of the DOM4J object model implementation. That is, it has not been registered using Configuration.registerExternalObjectModel().
With Saxon-PE and Saxon-EE this should be known by default, but with Saxon-HE it has to be registered explicitly. So we need to know more about what version of the Saxon software you are using and how it is initialized.
This question is very specific to Saxon, and the best strategy for getting answers to such questions is to ask on the forums at saxonica.plan.io. We check questions tagged "saxon" on StackOverflow from time to time, but it can take a few days before we notice them, and we don't give them priority.
i have and xsl file which is rendering the question on UI.
The Question are distributed in different catagory.
Now my requirement is to pass the parameter from java code to xsl file and on the basis of that parameter i would like to perform specific operation to generate the UI.
Can Any body help me out in suggesting how to pass parameter to XSL file from JAVA code ?
Example:
/form/A/Question-Category,
/form/B/Question-Category,
/form/c/Question-Category,
/form/D/Question-Category
A,B,C,D are categories which I will pass from java code and use that token to get my XPATh of question
Say if token passed from java code is B, then expression will be '/form/B/Question-Category' .
Now my hurdle is i dont know how to pass the parameter from java code and how i can use it in XSL?
Declare the parameter like this:
<xsl:param name="category"/>
Use it like this
select="/form/*[name()=$category]/Question-Category
Then pass it from Java like this (assuming you are using the JAXP API):
transformer.setParameter("category", "a");
I don't think this is a particularly smart XML document design by the way. I think the list of categories is data rather than metadata, so I would use <category name="A"> rather than <A> to define category A. But your course tutor may have other ideas (I assume this is a student exercise, because implementing a questionnaire usually is.)
I need to include an XSLT that exists in 2 variants, depending on a param value.
However, it's seems to be not possible to write an expression in the href attribute of the xsl:include element. My last trial looks like that:
< xsl:param name="ml-fmt" select="mono"/>
...
< xsl:include href="{$ml-fmt}/format.xsl"/>
The XSLT engine used is Saxon 9.2.0.6
Have anybody an idea about how I could do something close to that ?
As Dimitre has said you can't do it, but you can generate the XSLT file from scratch or slightly modify an existing XSLT file by inserting the node in the code preparing the transformation.
You can't.
If you know all possible xslt stylesheet modules to be included, you could use the xsl:use-when attribute in order to selectively include only some of them. However, xsl:use-when has its own limitations. To quote the XSLT 2.0 Spec:
"Any element in the XSLT namespace may have a use-when attribute whose value is an XPath expression that can be evaluated statically".
There is a way to achieve dynamic inclusion, but it requires some non-XSLT initialization:
The code (think C# or Java or ... your programming language) that invokes the transformation, can edit the DOM of the loaded (as XML) XSLT stylesheet and can set the value of the href attribute of any <xsl:import> element to the desired URL.
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.
I am trying to transform XML file twice with different XSLT files (Two step view). Is it possible to do so?
Example:
data.xml -> transformed by first.xsl -> result of first transformation (XML) -> transformed by second.xsl -> result of second transformation (HTML)
Unfortunately, with standards-compliant XSLT 1.0: no, this is not possible.
In XSLT 2.0, a template's return value may be used as input to another template; so an upgrade to XSLT 2.0 (which is easier to work with on many other fronts as well) would solve this limitation for you.
Another workaround is using the node-set extension function: but, being non-standard, this is obviously not supported everywhere identically: see http://www.xml.com/pub/a/2003/07/16/nodeset.html for details.
In XSLT 2.0 this is supported -- just capture in an <xsl:variable/> the result of the first transformation, then apply templates (possibly with different mode) to the top child (or any other descendents) of the xml document/fragment contained in the xsl:variable.
In XSLT 1.0 one has to use the xxx:node-set() extension, which converts the contents of the xsl:variable (which is of type RTF -- Result Tree Fragment) into a regular XML document/fragment.
This extension-function is quite standardized by EXSLT -- the "most standard" and widely implemented library of XSLT 1.0 extension functions.