How to select stylesheet to import based on content in XML - xslt

I'm trying to import a stylesheet based on the data source defined in the XML it is transforming.
XML1
<Root>
<Source>Facility A</Source>
</Root>
XML2
<Root>
<Source>Facility A</Source>
</Root>
I tried it like this:
<xsl:stylesheet>
<xsl:template match="/">
<xsl:choose>
<xsl:when test="Root/Source/text()='Facility A'">
<xsl:import href="Stylesheet for Facility A.xml"/>
</xsl:when>
<xsl:when test="Root/Source/text()='Facility B'">
<xsl:import href="Stylesheet for Facility B.xml"/>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
The transform just failed. Not sure how else to approach this.

This is a bit like saying that you want to import different modules into a Java program based on its input data: you're missing the fact that you can't read the input data until you have assembled/compiled the program.
In XSLT 3.0 you can invoke a stylesheet dynamically using the transform() function, but that may be more complicated than you actually need.
Another approach is to use a tool like XProc to control your workflow - or there are many other frameworks that enable you to orchestrate the execution of different stylesheets against different source documents. Camel seems to be popular at the moment, though I haven't used it, and I don't know how it compares with alternatives.

Related

Can I set <xsl:output/> attributes conditionally in an XSLT stylesheet?

I'm trying to automate some xslt transformations, and I need a way to set xsl:output attributes based on the content in the XML files being transformed.
Specifically, I want to look into the XML files, grab the lang attribute from the root element, and set attributes based on the lang value.
I've tried the following:
<xsl:param name="language">
<xsl:value-of select="//*/#lang"/>
</xsl:param>
<xsl:output method="xml" xmlns:saxon="http://icl.com/saxon" encoding="utf-8">
<xsl:choose>
<xsl:when test="$language != 'ja'">
<xsl:attribute name="saxon:character-representation"><xsl:value-of select="'native'"/></xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="saxon:character-representation"><xsl:value-of select="'hex'"/></xsl:attribute>
</xsl:otherwise>
</xsl:choose>
</xsl:output>
...but my parser informs me that the xsl:output element must be empty.
Is there a way to do something like this in the context of a style sheet, or am I going to have to manipulate these attributes at a higher level?
You've tagged this docbook, which I suspect is why you're still using the ancient Saxon 6.5.5 processor and its http://icl.com/saxon namespace. However, the docbook stylesheets can be made to work with a modern version of Saxon, which allows you to choose serialization attributes dynamically in an xsl:result-document instruction.
An alternative is to override the xsl:output properties from the Java API or command line. However, that's awkward in your case where you want to make the properties dependent on something in the source document.

How to prevent self closing tags as well empty tags after transforming

I have in an input file:
<a></a>
<b/>
<c>text</c>
I need to converting this to string. Using transformer I am getting below output:
<a/> <!-- Empty tags should not collapse-->
<b/>
<c>text</c>
If I use xslt and output method is "HTML", I get the below output:
<a></a> <!-- This is as expected-->
<b></b> <!-- This is not expected-->
<c>text</c>
I want the structure same as in input file. It is required in my application since I need to calculate index and it will be very difficult to change the index calution logic.
What would be the correct XSLT to use?
What XSLT processor? XSLT is merely a language to transform xml so "html output" is dependent on the processor.
I'm going to guess this first solution is too simple for you but i've had to use this to avoid processing raw html
<xsl:copy-of select="child::node()" />
as this should clone the raw input.
In my case, I have used the following to extract all nodes that had the raw attribute:
<xsl:for-each select="xmlData//node()[#raw]">
<xsl:copy-of select="child::node()" />
</xsl:for-each>
Other options:
2) Add an attribute to each empty node depending on what you want it to do later ie role="long", role="short-hand".
3)
Loop through each node (xsl:for-each)
<xsl:choose>
<xsl:when test="string-length(.)=0"> <!-- There is no child-->
<xsl:copy-of select="node()" />
</xsl:when>
<xsl:otherwise>
...whatever normal processing you have
</xsl:otherwise>
4) Redefine your problem. Both are valid XHTML/XML, so perhaps your problem can be reframed or fixed elsewhere.
Either way, you may want to add more information in your question so that we can reproduce your problem and test it locally.
P.S. Too much text/code to put in a comment, but that's where this would belong.
A possible alternative is to use disable-output-escaping like this:
<xsl:text disable-output-escaping="yes"><a></a></xsl:text>
But I understand that this is a dirty solution...

How to handle linked components in XSLT in tridion SDL Tridion 2011 SP1 using XSLT mediator

I am working on creating XSLT TBB for a component that has link to another component.
Consider my Component name is "A" which has link to another component "B".
Component A source looks like this:
<Content xmlns="Some UUID">
<Name xlink:type="simple" xlink:href="tcm:184-1897"
xmlns:xlink="http://www.w3.org/1999/xlink" xlink:title="B"></Name>
</Content>
Component B source is:
<Content xmlns="Some UUID">
<first>first filed</first>
<second>second field</second>
</Content>
Can any one help me how to write an XSLT TBB that outputs values from this linked Component?
Thank you.
In order to access fields from the linked component you will need to load it using the document function, keep i nmind that the linked component may be based on a different Schema, and as such have a different name space like this:
Component A
<Content xmlns="Some UUID">
<Name xlink:type="simple"
xlink:href="tcm:184-1897"
xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:title="B"/>
</Content>
Component B
<Content xmlns="Some Other UUID">
<Text>Some Value</Text>
</Content>
You can then transform the Component A and access the linked Component B as follows:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:main="Some UUID"
xmlns:link="Some Other UUID"
xmlns:xlink="http://www.w3.org/1999/xlink" >
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="LINKED_COMPONENT" select="document(//main:Name/#xlink:href)"/>
<xsl:value-of select="$LINKED_COMPONENT//link:Text"/>
</xsl:template>
</xsl:stylesheet>
Note that I have used "//" in my XPath to make the code easier to read, but this is not ideal from a performance stand point.
If for some reason you will not know what Schema (and therefore namespace) the linked Component will be based on, you can also use the $LINKED_COMPONENT//*[local-name()='Text'] notation, but this again will introduce a performance hit.
Please explain what you mean by "handle this component linking".
Do you mean that you want to access this linked component and its fields within your TBB on the content manager side, or do you mean that you want to output an anchor tag in your HTML that will link to the other component on your website?
To output an image that your Component links to, have a look at this:
http://yoavniran.wordpress.com/2009/07/11/implementing-the-xslt-mediator-part-1
<xsl:element name="img">
<xsl:attribute name="src">
<xsl:value-of select="simple:image/#xlink:href"/>
</xsl:attribute>
</xsl:element>
Edit: To output additional fields of a linked Component, see this section: http://yoavniran.wordpress.com/2009/07/11/implementing-the-xslt-mediator-part-1/#complink
An example from there:
<xsl:attribute name="alt">
<xsl:value-of select="document(simple:image/#xlink:href)/tcm:Component/tcm:Data/tcm:Metadata/image:Metadata/image:altText"/>
</xsl:attribute>
So this loads the Multimedia Component and then extracts a value from a Metadata field.
Here is XSLT code to extract some fields from multi-value component links. Keep in mind that component links belongs to the same schema.
<!-- language: xml -->
<xsl:for-each select="base:componentLink">
<xsl:element name="div">
<xsl:variable name ="LinkedComponent" select="document(./#xlink:href)"></xsl:variable>
<xsl:value-of select="$LinkedComponent/tcm:Component/tcm:Data/tcm:Content/linked:Teaser/linked:LinkText"/>
</xsl:element>
</xsl:for-each>

Detecting if a node exists?

I have a set of data called <testData> with many nodes inside.
How do I detect if the node exists or not?
I've tried
<xsl:if test="/testData">
and
<xsl:if test="../testData">
Neither one works. I'm sure this is possible but I'm not sure how. :P
For context the XML file is laid out like this
<overall>
<body/>
<state/>
<data/>(the one I want access to
</overall>
I'm currently in the <body> tag, though I'd like to access it globally. Shouldn't /overall/data work?
Edit 2:
Right now I have an index into data that I need to use at anytime when apply templates to the tags inside of body. How do I tell, while in body, that data exists? Sometimes it does, sometimes it doesn't. Can't really control that. :)
Try count(.//testdata) > 0.
However if your context node is textdata and you want to test whether it has somenode child or not i would write:
<xsl:if test="somenode">
...
</xsl:if>
But I think that's not what you really want. I think you should read on different techniques of writing XSLT stylesheets (push/pull processing, etc.). When applying these, then such expressions are not usually necessary and stylesheets become simplier.
This XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="text()"/> <!-- for clarity only -->
<xsl:template match="body">
<xsl:if test="following-sibling::data">
<xsl:text>Data occurs</xsl:text>
</xsl:if>
<xsl:if test="not(following-sibling::data)">
<xsl:text>No Data occurs</xsl:text>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Applied to this sample:
<overall>
<body/>
<state/>
<data/>(the one I want access to
</overall>
Will produce this correct result:
Data occurs
When applied to this sample:
<overall>
<body/>
<state/>
</overall>
Result will be:
No Data occurs
This will work with XSL 1.0 if someone needs...
<xsl:choose>
<xsl:when test="/testdata">node exists</xsl:when>
<xsl:otherwise>node does not exists</xsl:otherwise>
</xsl:choose>

Create node set and pass as a parameter

Using XSLT 1.0, I'm trying to essentially create a small node set and then pass it as a parameter to a template, something like the following:
<xsl:call-template name="widget">
<xsl:with-param name="flags">
<items>
<item>widget.recent-posts.trim-length=100</item>
<item>widget.recent-posts.how-many=3</item>
<item>widget.recent-posts.show-excerpt</item>
</items>
</xsl:with-param>
</xsl:call-template>
The idea is that then from within the widget template I could write something like:
<xsl:value-of select="$flags/item[1]" />
Obviously I get compile errors.. how can I achieve this sort of thing?
There is a way (non-standard) in XSLT 1.0 to create temporary trees dynamically and evaluate XPath expressions on them, however this requires using the xxx:node-set() function.
Whenever nodes are dynamically created inside the body of an xsl:variable or an xsl:param, the type of that xsl:variable / xsl:param is RTF (Result Tree Fragment) and the W3 XSLT 1.0 Spec. limits severyly the kind of XPath expressions that can be evaluated against an RTF.
As a workaround, almost every XSLT 1.0 vendor has their own xxx:node-set() extension function that takes an RTF and produces a normal node-set from it.
The namespace to which the xxx prefix (or any other prefix you choose) is bound is different for different vendors. For MSXML and the two .NET XSLT processor it is: "urn:schemas-microsoft-com:xslt". The EXSLT library uses the namespace: "http://exslt.org/common". This namespace EXSLT is implemented on many XSLT 1.0 processors and it is recommended to use its xxx:node-set() extension, if possible.
Here is a quick example:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:ext="http://exslt.org/common"
exclude-result-prefixes="ext msxsl"
>
<xsl:template match="/">
<xsl:variable name="vTempRTF">
<a>
<b/>
</a>
</xsl:variable>
<xsl:copy-of select="ext:node-set($vTempRTF)/a/*"/>
</xsl:template>
</xsl:stylesheet>
Well, I managed to get around this in the following way:
First add a custom namespace to your stylesheet, e.g. xmlns:myns="http://my.ns.com"
Then define the nodeset at the top of the stylesheet:
<myns:recent-posts-flags>
<item>widget.recent-posts.trim-length=100</item>
<item>widget.recent-posts.how-many=3</item>
<item>widget.recent-posts.show-excerpt</item>
</myns:recent-posts-flags>
Then reference in the following way:
<xsl:call-template name="widget">
<xsl:with-param name="flags" select="document('')/*/myns:recent-posts-flags" />
</xsl:call-template>
This works, but it would still be ideal for me to define the node-set within the <xsl:with-param> tag itself, as in the first example I gave.. anyone think that would be possible?