I have to extract some records from a dynamically generated xml document whose hierarchy will be of a pre-defined format but the depth of the hierarchy will not be known. I am working in a .net framework and i know that we can use .net methods from xsl and that we need to use a recursive function in this situation in conventional programming paradigm. but i dont know how to work round it using pure xsl, which is what i need to do in this particular case.
Use // before node names example:
<xsl:template match="//temp">
<!--do whatever you want-->
</xsl:template>
This will apply to all the elements that are having name temp appearing in any hierarchy ..
Related
I am working with a project where I need to create a html-file supporting several languages from an xslt-transformation. I have read several articles around this, and also looked at previous questions here in stackoverflow, like this one:
xslt localization
And the solution to put the translations in a separate xml document works just as I want it to. But I wonder if there exists any standardized/best practice for the "translate.xml" file? In the post referenced above, the following is given as an example:
<strings>
<string language="en" key="ContactDetails">Contact Details</string>
<string language="sv" key="ContactDetails">Kontaktuppgifter</string>
[...]
</strings>
As I said, the solution suggested with using keys retrieve the strings from the transalte.xml works as I want it to, but I like to use standards if they are available, so my question is if there is a standardized schema for these types of xml files, or some kind of best practice on the naming of tags etc in such a "translate.xml"?
Good question!
Yes, there is a standardized way of dealing with languages. Use the xml:lang attribute. The namespace magically exists in any XML document and is part of the core XML specification. It is defined to take a language specifier according to RFC-4646. These are the often-seen specifiers like en, en-US, es, es-BR, specifying the main language and the language variant.
The way it works is a bit like namespaces. If you define it on an element, then it is inherited by all descendants of that element, unless you redefine it, or undefine it to denote language-indepent elements.
For instance:
<text xml:lang="en">
We are
<t xml:lang="en-US">organizing</t>
<t xml:lang="en-GB">organising</t>
a conference on the effects of
<t xml:lang="en-US">color</t>
<t xml:lang="en-GB">colour</t>
in December this year.
</text>
Using a query language, i.e. XSLT, with a copy-idiom, this works excellently together with the lang() function, which takes the applicable language from the nearest ancestor or self and returns boolean true if found. It will also find the language variant like en-US if you set the main language, like en.
The following assumes XSLT 1.0, but works with 2.0 and 3.0 as well (this code was kindly corrected by Michael, see comments):
<!-- match English US language and default en -->
<xsl:template match="t[lang('en-US') or lang('en')">
<xsl:apply-templates />
</xsl:template>
<!-- remove any other <t> -->
<xsl:template match="t" />
Note: always set a default language on the outermost element, as lang() will return false if no language is found at all. You could test for this with the expression lang(''), which will return false only if no language was set at all.
About your XML files, if you have one file per language, and don't mix and match like suggested above, you can still use the same approach by setting xml:lang on the root element. Since this will then be inherited throughout the whole tree, you can still use the lang() function.
Is it possible to create XML nodes using XPath?
Assume i got the following XML:
<data>
<someValue1></someValue2>
<someValue2></someValue2>
<someArray>
<val></val>
<val></val>
<val></val>
</someArray>
</data>
What I am trying to do is getting a node inside the XML using XPath (e.g. /data/someValue1) and setting the value of the node. This is of course easy to achieve with any language and framework supporting XML + XPath.
But when my XPath expression is pointing to a non existing node I want to somehow create this node. Even if the XPath expression is more complex (e.g. /data/someArray/val[5]).
At best this automatic node creation should be supported by some library. Is there an easy way for what I am trying to achieve? Currently I am using C++ with Qt and the QXmlQuery class.
Is it possible to create XML nodes using XPath?
No, it isn't. XPath can only select existing nodes.
If you want to create new nodes, you need XSLT or XQuery.
But even then, creating a node that satisfies a given path like /a/b/c/foo[5] is non-trivial, and not something that the languages can do automatically.
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 have some XML/TEI documents, and i'm writing an XSLT 2.0 to extract their content.
Almost all TEI documents has no namespace, but one has the default namespace (xmlns="http://www.tei-c.org/ns/1.0").
So all documents has the same aspect, with unqulified tags like <TEI> or <teiHeader>, but if I try to extract the content, all works with "non-namespaced-documents", but nothing (of course) is extracted from the namespaced-document.
So i used the attribute xpath-default-namespace="http://www.tei-c.org/ns/1.0" and now (of course) the only document working is the namespaced one.
I can't edit documents at all, so what I'm asking is if there's a way to change dynamically the xpath-default-namespace in order to make work xpaths like //teiHeader both with namespaced and non-namespaced documents
If you are using XSLT 2.0, then you do have the option for a wildcard match for the namespace in a node test.
e.g. //*:teiHeader
http://www.w3.org/TR/xpath20/#node-tests
A node test can also have the form
*:NCName. In this case, the node test is true for any node of the principal
node kind of the step axis whose local
name matches the given NCName,
regardless of its namespace or lack of
a namespace.
This is functionally equivalent to Dimitre Novatchev's example, but a little shorter/easier to type.
However, this will only work in XSLT/XPATH 2.0.
There isn't really a clean way to do precisely what you are asking. However, there are workarounds available. You could use a two stage process whereby you strip the namespace from the document if it's present and then pass it through the same templates for all content.
There is a good example (in XSLT 1) of doing this in the DocBook XSLT. Take a look at html/docbook.xsl and common/stripns.xsl
Basically, you would need to assign the result of stripping the namespace to a variable and then call your existing templates (for the non namespaced) content but select the variable.
It is ugly, but this gives you what you want:
//*[name()='teiHeader']
If you use this style for all location steps in any XPath expression, the XPath expressions will select elements only by name, regardless whether or not the elements belong to any namespace.
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.