Conditionaly assign variable to match path in xslt template - java

I want to dynamically build match in xslt template. I use xls fo and apache fop and saxon9he. In best way I want to pass param from java but firstly I try set this in xslt.
When i create variable example like this:
<xsl:variable name="testPath" select="/abc:Files/def:Description" />
also work correctly if I try use this in apply-templates:
<xsl:apply-templates select="$testPath/qwe:Test"/>
But i want dynamically set testPath variable. I try use choose tag:
<xsl:variable name="testPath">
<xsl:choose>
<xsl:when test="$versionNo = '2'">
<xsl:value-of select="/abc:Files/def:Description" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="/abc:Files/def:Names" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
But this approach not working and when I try use this variable:
<xsl:apply-templates select="$testPath/qwe:Test"/>
I get this error:
Error evaluating ((attr{table-layout=...}, ...)) on line 82 column 21
of pdf_gen.xsl: SXCH0003: org.apache.fop.fo.ValidationException:
"fo:table-body" is missing child elements. Required content model:
marker* (table-row+|table-cell+) (See position 82:21):
file:/C:/Users/SuperUser/workspace/project/xls-editor/target/classes/pdf/xsl/pdf_gen.xsl:82:21:
"fo:table-body" is missing child elements. Required content model:
marker* (table-row+|table-cell+) (See position 82:21)
In best option I want to pass $testPath variable from Java as param, example:
transformer.setParameter("testPath ", "/abc:Files/def:Description");
and use in xslt
<xsl:param name="testPath "/>
and apply in templates:
<xsl:apply-templates select="$testPath/qwe:Test"/>
but i get following error:
Type error evaluating ($testPath) in
xsl:apply-templates/#select on line 74 column 60 of
pdf_gen.xsl: XPTY0019: The required item type of the
first operand of '/' is node(); the supplied value
u"/abc:Files/def:Description" is an atomic value
Why is not any solution working how to achieve it?

As you use Saxon 9 you do use at least XSLT 2 with XPath 2 and there I would suggest to implement the variable selection in XPath with e.g.
<xsl:variable name="testPath" select="if ($versionNo = '2') then /abc:Files/def:Description else /abc:Files/def:Names"/>
That way your
<xsl:apply-templates select="$testPath/qwe:Test"/>
should work fine to process the qwe:Test child element(s) of the previously selected def:Description or def:Names elements.
It is of course also possible to use e.g.
<xsl:apply-templates select="(if ($versionNo = '2') then /abc:Files/def:Description else /abc:Files/def:Names)/qwe:Test"/>
or do e.g.
<xsl:apply-templates select="(/abc:Files/def:Description[$versionNo = '2'], /abc:Files/def:Names[$versionNo != '2'])/qwe:Test"/>

I'm dealing with a similar problem.
It seems to me that variables visibility is only inside the <xsl:choose> block and I could suggest you to make templates blocks and to call them in this way:
<xsl:choose>
<xsl:when test="$versionNo = '2'" >
<xsl:call-template name="TemplateOne">
<xsl:with-param name="testPath" select="'/abc:Files/def:Description'" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="TemplateTwo">
<xsl:with-param name="testPath" select="'/abc:Files/def:Names'" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
with templates definition as:
<xsl:template name="TemplateOne">
<xsl:param name="testPath" />
...
bla bla bla
remember to use variable in this template as "{$testPath}"
...
</xsl:template>

Related

XSLT How to iterate a Java List with XALAN

I am trying to iterate over an array using XSLT in order to get a final text looking like this:
array1.firstElement
array1.secondElement
I have this in my XSLT:
<xsl:param name="nombreEmpresaDebe" />
And I put this in my transform method:
transformer.setParameter("nombreEmpresaDebe", listaNombreEmpresaDebe);
renglones/reglon is the name of my node and I am already iterating over it with the following code:
<xsl:for-each select='renglones/renglon'>
<label>
<xsl:value-of select="rubro" />
</label>
<label>
<xsl:value-of select="$nombreEmpresaDebe"/>
</label>
<xsl:for-each>
Now I have to get $nombreEmpresaDebe elements which is a java.util.List but I have no clue about doing it in the same for-each. Does anyone know how to do it?
I need something like this:
<renglon1>
<rubro>rubro1</rubro>
<nombreEmpresaDebe>firstElement</nombreEmpresaDebe>
</renglon1>
<renglon2>
<rubro>rubro2</rubro>
<nombreEmpresaDebe>secondElement</nombreEmpresaDebe>
</renglon2>
You can accomplish this by calling a recursive template. For example
<xsl:call-template name="outputList">
<xsl:with-param name="list" select="$nombreEmpresaDebe"/>
<xsl:with-param name="index" select="0"/>
</xsl:call-template>
Where the template is defined like
<xsl:template name="outputList">
<xsl:param name="list"/>
<xsl:param name="index"/>
<xsl:if test="number($index) < java:size($list)">
<label>
<xsl:value-of select="$nombreEmpresaDebe"/>
</label>
<xsl:call-template name="outputList">
<xsl:with-param name="list" select="$list"/>
<xsl:with-param name="index" select="number($index)+1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
I should note that my answer does not combine this with the iteration of the other nodelist you are handling, but this is the basic List iterating technique that could be combined with that. Also, I realize this is 3 years late, but someone else my find this question and benefit from having an answer.

getting attributes of all the parent in xslt

I have following xml structure
<comp name = "a">
<subcomp1 name = "a1">
<subcomp2 name = "a2"/>
</subcomp1>
<subcomp3 name="a3/>
</comp>
If I try the following syntax,
<xsl:value-of select="#name" />
gives attribute value of name when I am in perticular tag. ie when I am at
<comp> #name = a, at<subcomp2> it is a2
But I want to get all the attribute value including the parents. ie when I am at
<subcomp2> I want value a->a1->a2
<subcomp1> a->a1
<subcomp3> a->a3
<xsl:value-of select="..\#name" />
gives only one parent above. So please let me know the solution for the same
The expression you want is ancestor-or-self::*/#name
If you are using XSLT 2.0, then xsl:value-of returns all matching attributes, so you can just do this to list them, for example
<xsl:value-of select="ancestor-or-self::*/#name" separator="-" />
However, in XSLT 1.0, xsl:value-of will only return the value of the first one. So, you can use xsl:for-each instead
<xsl:for-each select="ancestor-or-self::*/#name">
<xsl:if test="position() > 1">-</xsl:if>
<xsl:value-of select="." />
</xsl:for-each>
Or maybe this...
<xsl:for-each select="ancestor-or-self::*">
<xsl:if test="position() > 1">-</xsl:if>
<xsl:value-of select="#name" />
</xsl:for-each>

Process multiple XSLTs and pass the result to another XSLT for transformation in Java

I have a requirement in my code and it is as follows:
The
Parameters in my Java program are passed to an XSLT(X1) for processing. The X1 returns only one String value.
Another set of Parameters are passed to another XSLT(X2) for processing.The X2 also returns only one String value.
After Transformations the XSLT results need to be passed to another XSLT(X3) which would use the value as Variables and generate result basis the results from X1 and X2.
As far as i have been studied the XSLT variables values cannot be modified once stored. and also that these values are received as parameters when calling <xsl:apply-template name="someTemplate" > and with parameter tags.
i can implement this functionality by retrieving the result into String in Java and again Passing them as parameters to the next template. but i wanted to know if the same can be done directly via XSLT.
Please help
Edit: Xalan removed as tag
i can implement this functionality by retrieving the result into
String in Java and again Passing them as parameters to the next
template. but i wanted to know if the same can be done directly via
XSLT.
Yes, here is an XSLT 2.0 example:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pP1" select="2"/>
<xsl:param name="pP2" select="3"/>
<xsl:param name="pP3" select="5"/>
<xsl:variable name="vPass1">
<xsl:apply-templates mode="pass1">
<xsl:with-param name="pP1" select="$pP1"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:variable name="vPass2">
<xsl:apply-templates mode="pass2">
<xsl:with-param name="pP2" select="$pP2"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:variable name="vPass3">
<xsl:apply-templates mode="pass3">
<xsl:with-param name="pP3" select="$pP3"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:template match="/">
<xsl:value-of select="$vPass1 + $vPass2 + $vPass3"/>
</xsl:template>
<xsl:template match="/*" mode="pass1">
<xsl:param name="pP1" as="xs:integer"/>
<xsl:value-of select="sum(*[. mod $pP1 = 0])"/>
</xsl:template>
<xsl:template match="/*" mode="pass2">
<xsl:param name="pP2" as="xs:integer"/>
<xsl:value-of select="sum(*[. mod $pP2 = 0])"/>
</xsl:template>
<xsl:template match="/*" mode="pass3">
<xsl:param name="pP3" as="xs:integer"/>
<xsl:value-of select="sum(*[. mod $pP3 = 0])"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>
three passes are performed, each with its own parameters. Each pass computes the sum of num elements, whose value is multiple of the supplied parameter. Finally, the results of the three passes are summed and returned as the final result:
63
Exactly the same transformation can be run with an XSLT 1.0 processor, with the exception that any string "as='xs:integer'" must be removed from the code.

Why can't I use values of nodes I retrieve by using exsl:node-set/set:distinct in an XPath-Expression?

In a xslt-stylesheet I'm using the methods exsl:node-set and set:distinct to access and filter unique nodes from a variable that contains a result tree fragment.
I can write the values of these nodes into my output file, example:
<xsl:variable name="myNodes">
<xsl:call-template name="getNodes"/>
</xsl:variable>
<xsl:for-each select="set:distinct(exsl:node-set($myNodes)/key)">
<xsl:value-of select="."/>
</xsl:for-each>
The values of the keys are written in the output, just as expected.
However, if I try to use the values in an XPath expression, it fails:
<xsl:for-each select="set:distinct(exsl:node-set($myNodes)/key)">
<xsl:variable name="result" select="/tree//somenode[#key = current()]"/>
<xsl:value-of select="$result"/>
</xsl:for-each>
Now, the output is empty, whereas I know that there is a "somenode" in my input-xml that should be selected by the XPath expression and it's value is not empty.
Now my question is: Why does this happen?
I'm using Java 1.6, Xerces 2.7 and Xalan 2.7.
update:
as requested, some data for the example:
xml doc contains:
<tree>
<somenode key="123"/>
<num>123</num>
<num>0815</num>
</tree>
the getNodes template:
<xsl:template name="getNodes">
<xsl:for-each select="/tree/num">
<xsl:element name="key">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</xsl:template>
Here is a transformation that does something close to what you want:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:set="http://exslt.org/sets"
xmlns:exsl="http://exslt.org/common"
>
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="/">
<xsl:variable name="myNodes">
<xsl:call-template name="getNodes"/>
</xsl:variable>
<xsl:variable name="vDoc" select="/"/>
<xsl:for-each select="set:distinct(exsl:node-set($myNodes)/key)">
<xsl:variable name="result" select="$vDoc/tree//somenode[#key = current()]"/>
<xsl:copy-of select="$result"/>
</xsl:for-each>
</xsl:template>
<xsl:template name="getNodes">
<xsl:for-each select="/tree/num">
<xsl:element name="key">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<tree>
<somenode key="123"/>
<num>123</num>
<num>0815</num>
</tree>
the wanted result is produced:
<somenode key="123"/>
Do note:
The source XML document cannot direstly be accessed inside the <xsl:for-each>, because this instruction sets the current node to a node in another document -- the temporary tree created by exsl:node-set().
For this reason we capture the source XML document in a variable $vDoc. We access the source XML document inside the <xsl:for-each> via this variable.
The element <somenode key="123"/> has no text-node descendents and hence no string value. Using <xsl:value-of> on it will not produce any output. This is why we use <xsl:copy-of> here -- it copies the complete element and we see the result.
This stylesheet does the same you want without extensions:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="NumByValue" match="num" use="."/>
<xsl:template match="num[count(.|key('NumByValue',.)[1])=1]">
<xsl:copy-of select="../somenode[#key=current()]"/>
</xsl:template>
</xsl:stylesheet>
Output:
<somenode key="123" />

how to create hyperlinks when transforming xml to xsl-fo using xlst?

I would like to transform any http/s based url inside random text to be automatically tagged with xsl-fo , where the random text might contains one or more http/s based url.
So the http/s url is not part of an attribute or the only content of a node, but part of text within a node.
For example: the source
<misc>
<comment>Yada..yada..yadda, see http://www.abc.com.
Bla..bla..bla.. http://www.xyz.com</comment>
</misc>
Will be transform into something like:
<fo:block>
Yada..yada..yadda, see <fo:basic-link external-destination="http://www.abc.com">http://www.abc.com</fo:basic-link>.
Bla..bla..bla.. <fo:basic-link external-destination="http://www.xyz.com">http://www.xyz.com</fo:basic-link>
<fo:/block>
The library we are using is Apache FOP and Xalan-J.
If you have to use a pure XSLT method, you could use this:
<xsl:template match="comment">
<fo:block>
<xsl:call-template name="dig-links">
<xsl:with-param name="content" select="."/>
</xsl:call-template>
</fo:block>
</xsl:template>
<xsl:template name="dig-links">
<xsl:param name="content" />
<xsl:choose>
<xsl:when test="contains($content, 'http://')">
<xsl:value-of select="substring-before($content, 'http://')"/>
<xsl:variable name="url" select="concat('http://', substring-before(substring-after(concat($content, ' '), 'http://'), ' '))"/>
<fo:basic-link>
<xsl:attribute name="external-destination">
<xsl:choose>
<xsl:when test="substring($url, string-length($url), 1) = '.'">
<xsl:value-of select="substring($url, 1, string-length($url)-1)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$url"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:value-of select="$url"/>
</fo:basic-link>
<xsl:call-template name="dig-links">
<xsl:with-param name="content" select="substring-after($content, $url)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$content"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
It's not a perfect solution however, thus if you have typos such as two dots at the end of a url, the external-destination attribute will get one.
I don't know what xsl:fo means, but if you have access to the xslt file that actually does the conversion, you might convert it yourself by using xml string functions mentioned here. If you have access to the transformed output only, you still can
apply another xslt that does what you want or
transform it yourself using SAX and using the java regex functions

Categories

Resources