Define XSL variable without type changing - java

I have a variable in my XSL file. Java returned java.util.List into this variable
<xsl:variable name="testVariable" select="java:getJavaList()"/>
After that this vairable is used in Java by method that takes java.util.List as parameter
<xsl:variable name="anotherVariable">
<xsl:value-of select="java:useTestVariable($testVariable)"/>
</xsl:variable>
Now requirements were changed to set testVariable value depending on particular condition. I tried this approach
<xsl:variable name="testVariable">
<xsl:choose>
<xsl:when test="contains($oneMoreVariable, '%')">
<xsl:value-of select="java:getValue('%')" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="java:getDefaulValue()" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
The problem is that if I define variable in this way, it contains not List, but tree fragment. And I get an exception
For extension function, could not find method
MyClassName.useTestVariable([ExpressionContext,] #RTREEFRAG)
Can somebody please advice what is the correct way to define variable value basing on some condition so, that variable's type would not be changed with tree fragment?

I don't think there's any way to do this in XSLT 1.0 as by definition any xsl:variable with content rather than a select must be a result tree fragment. But since you're able to make calls into Java you can put the conditional logic into a Java function, then call that from the variable's select.

Have you verified the resuts of java:getValue('%') and java:getDefaulValue() , if these results are coming out be what you expect and still it is a fragement refer this link https://blogs.oracle.com/rammenon/entry/result_tree_fragments_and_node

Related

How to implement saxon OutputURIResolver in Java?

I am new to Java. I have a similar scenario as [Catch output stream of xsl result-document but I am not understanding what to pass as href and base parameter..
My XSLT (result-document) as follows:
<xsl:template match="/" mode="create-text-file">
<xsl:param name="book-name" tunnel="yes"/>
<xsl:result-document href="{$book-name}.xml"
doctype-public=" test"
doctype-system="test.dtd">
<xsl:apply-templates/>
</xsl:result-document>
</xsl:template>
Another:
<xsl:result-document href="{$book-name}.opf"
doctype-public=""
doctype-system=""
indent="yes">
<xsl:apply-templates mode="#current"/>
</xsl:result-document>
Parameter book-name is getting as :
<xsl:template match="document-node()">
<xsl:variable name="book-name" select="tps:get-file-name(document-uri(/))"/>
Can you please explain me with this result-documents?
Thanks in advance.
OutputURIResolver is a callback: that means you supply the implementation of the resolve() method, and Saxon calls your implementation. So you don't need to worry about what to pass as the base and href arguments, because you don't call the method, Saxon does. It will typically supply the href argument as the (expanded) value of the href attribute of your call to xsl:result-document, and the base value will be the "base output URI", which defaults to the file nominated as the principal output of the transformation (-o option on the command line).

XSLT check if String is inside a set

I am trying to check if a String is contained within a set. I have an Excel sheet that I convert to an xml file; example:
Excel sheet on left and converted sheet on right (RowData.xml):
So I have an xml file where those set of numbers may or may not be there. For example, the source xml may look like this:
Source.xml:
<Data>
<Number>5556781234</Number>
<Number>5556781235</Number>
<Number>5556781236</Number>
</Data>
As you see it can stop anywhere. The source xml file may have all the numbers listed in RowData.xml or it may have only 1 or more. So my question is, how would I check for that in my xslt file?
I want to do this:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- This is the Excel sheet converted to an XML file -->
<xsl:param name="sheet-uri" select="'RowData.xml'"/>
<xsl:param name="sheet-doc" select="document($sheet-uri)"/>
<xsl:template match="Data">
<xsl:for-each select="Data/Number">
<xsl:variable name="continue" select="$sheet-doc//Sheet/Row[Number = current()]/Continue"/>
<xsl:if test="">
<!-- Check the Source.xml against the RowData.xml and
see if the set contains any "No"'s in it. -->
<!-- If it does then don't do the following -->
<Data2>
<Number><xsl:value-of select="Number"/></Number>
<Timestamp>125222</Timestamp>
</Data2>
</xsl:if>
</xsl:for-each>
</xsl:template>
So basically, before making the <Data2> element, check the numbers in Source.xml and see if any of those numbers have a value of No for the column Continue in RowData.xml. I don't know how to make the if statement above. I know there's a contains() function in xslt; however, I don't know how I can use it here.
Is this possible? Please let me know if anything was confusing. Thanks in advance!
check the numbers in Source.xml and see if any of those numbers have a value of No for the column Continue in RowData.xml.
You can take advantage of XSLT's "existential equal" operator here:
test="doc('source.xml')/Data/Number =
$sheet-doc//Sheet/Row[Continue='No']/Number"
Essentially, if A and B are sets of values, then A = B returns true if some value in A is equal to some value in B.
I would suggest you use the key mechanism - esp. if you're using XSLT 2.0.
Define a key as:
<xsl:key name="row" match="Row" use="Number" />
then do:
<xsl:template match="/Data">
<xsl:for-each select="Number[not(key('row', ., $sheet-doc))]">
<Data2>
<xsl:copy-of select="."/>
<Timestamp>125222</Timestamp>
</Data2>
</xsl:for-each>
</xsl:template>
This selects only Numberelements that do not have a corresponding Row in the RowData.xml document.

Xalan and the document() function

Since I have spent almost a full day now on debugging this, I hope to get some valuable insight on SO on following problem:
I am running an XSL Transformation on an input document, my stylesheet loads an external XML-Document which contains lookup values I need to do some comparisons.
I am loading the external document like this:
<xsl:variable name="dictionary"
select="document('myDict.xml', document(''))/path/to/LookupElement" />
LookupElement is an element which contains the complete XML-Fragment I need to access.
Throughout the stylesheet various comparison expressions are accessing $dictionary.
Now, what happens is, that the transformation with this document() function call in place takes about 12 (!) minutes using Xalan (2.7.?, latest version, downloaded from the Apache website, not the one contained in the JRE).
The same stylesheet without the document() call (and without my comparisons accessing data in $dictionary) completes in seconds.
The same stylesheet using Saxon-B 9.1.0.8 completes in seconds as well.
Information: The external document has 25MB(!) and there is no possibility for me to reduce its size.
I am running the transformations using the xslt-Task of ant under JRE 6.
I am not sure if this has anything to do with above mentioned problem: Throughout my stylesheet I have expressions that test for existence of certain attributes in the external XML-Document. These expressions always evaluate to true, regardless of whether the attributes exist or not:
<xsl:variable name="myAttExists" select="boolean($dictionary/path/to/#myAttribute)"/>
I am at the end of my wits. I know that Xalan correctly reads the document, all references go to $dictionary, so I am not calling document() multiple times.
Anybody any idea?
Edit:
I have removed the reference to the XML-Schema from the external XML-Document to prevent Schema-Lookups of Xalan or the underlying (Xerces) Parser.
Edit:
I have verified that myAttExists will always be true, even if specifiying an attribute name that for sure does not exist in the entire external XML-Document.
I have even changed the above expression to:
<xsl:variable name="myAttExists" select="count($dictionary/path/to/#unknownAttribute) != 0"/>
which still yields true.
Edit:
I have removed the call to the document() function and all references to $dictionary for testing purposes. This reduces transformation runtime with Xalan to 16 seconds.
Edit:
Interesting detail: The Xalan version shipped with Oxygen 12.1 completes within seconds loading the external XML-Document. However, it also evaluates the existence of attributes incorrectly...
Edit:
I have the following variable declaration which always yields true:
<xsl:variable name="expectedDefaultValueExists">
<xsl:choose>
<xsl:when test="#index">
<xsl:value-of select="boolean($dictionary/epl:Object[#index = $index]/#defaultValue)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="boolean($dictionary/epl:Object[#index = $index]/epl:SubObject[#subIndex = $subIndex]/#defaultValue)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
Is this possible in XSLT/XPath 1.0? $index and $subIndex are calculated from the #index and #subIndex attributes of the context node. I want to load the defaultValue attribute from the external XML-Document which has an equal index and/or subIndex.
Is it possible to use variables in predicates in XPath 1.0? This works in XPath 2.0.
Regarding the incorrect variable assignment, I don't believe in a parser (Xalan) issue anymore, since PHPs XSLTProcessor does the same. It must be an issue in the variable declaration...
This only answers the last part of the question, but it's getting too unwieldy for comments...
I have the following variable declaration which always yields true when used as the test of an xsl:if or xsl:when:
<xsl:variable name="expectedDefaultValueExists">
<xsl:choose>
<xsl:when test="#index">
<xsl:value-of select="boolean($dictionary/epl:Object[#index = $index]/#defaultValue)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="boolean($dictionary/epl:Object[#index = $index]/epl:SubObject[#subIndex = $subIndex]/#defaultValue)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
In XSLT 1.0 a variable with a body rather than a select always becomes a "result tree fragment", in this case with a single text node child that will contain the string "true" or "false" as appropriate. Any non-empty RTF is considered true when converted to boolean.
In XSLT 2.0 it's a similar story - 2.0 doesn't distinguish between node sets and result tree fragments, but still the variable will be a "temporary tree" with a single text node child whose value is the string "true" or "false", and both these trees are true when converted to boolean. If you want to get an actual boolean value out of the variable then you need to change two things - add as="xs:boolean" to the variable declaration and use xsl:sequence instead of xsl:value-of:
<xsl:variable name="expectedDefaultValueExists" as="xs:boolean">
<xsl:choose>
<xsl:when test="#index">
<xsl:sequence select="boolean($dictionary/epl:Object[#index = $index]/#defaultValue)"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="boolean($dictionary/epl:Object[#index = $index]/epl:SubObject[#subIndex = $subIndex]/#defaultValue)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
The xsl:value-of instruction converts the result of its select into a string and constructs a text node containing that string. The xsl:sequence instruction simply returns the value from the select directly as whatever type it happens to be.
But there are simpler ways to achieve the same thing. In XPath 2.0 you can do if/then/else constructs directly in the XPath
<xsl:variable name="expectedDefaultValueExists"
select="if (#index)
then $dictionary/epl:Object[#index = $index]/#defaultValue
else $dictionary/epl:Object[#index = $index]/epl:SubObject[#subIndex = $subIndex]/#defaultValue" />
In 1.0 you need to be slightly more creative
<xsl:variable name="expectedDefaultValueExists"
select="(#index and $dictionary/epl:Object[#index = $index]/#defaultValue)
or (not(#index) and $dictionary/epl:Object[#index = $index]/epl:SubObject[#subIndex = $subIndex]/#defaultValue)" />

XSLT gathering data

I have a simple issue that I can't really find a workaround to and I need your help.
The main problem is, that while process an input XML there are various places where I need to "gather" information. This means all I really have to do is call a special template with parameters like so:
<xsl:template name="append-section">
<xsl:param name="id" />
<xsl:param name="title" />
<!-- more code here -->
</xsl:template>
Lets say this template is called 12 times during the XSLT procedure. At the end of the conversion I want to write this data to a file.
I have tried to appen this data to a global variable and then write the result to the file. Only to realise the variables are not really variables in XSLT. This solution did not work.
Second solution was to use the xsl:result-document with one temp file. This solution would have done something like always copying the previous content of the file to itself, but also appending the new data something like this:
<xsl:template name="append-section">
<xsl:param name="id" />
<xsl:param name="title" />
<xsl:result-document method="html" href="tmp/tmp.html">
<xsl:value-of select="document(tmp.html)" />
<xsl:element name="li">
<xsl:element name="a">
<xsl:attribute name="class">
<xsl:value-of select="'so-dropdown-page-menu-list-button'" />
</xsl:attribute>
<xsl:attribute name="href">
<xsl:value-of select="'#'" />
<xsl:value-of select="$id" />
</xsl:attribute>
<xsl:value-of select="$title" />
</xsl:element>
</xsl:element>
</xsl:result-document>
</xsl:template>
This code might not be perfect, but I had to realise unfortunatly that the following exception was thrown:
Cannot write more than one result document to the same URI
This solution also seems to be invalid.
So my question is this: How can I implement this simple issue? Gather the data from various places and write them to a file at the end of the transformation.
I use Saxon.
You need to structure your code according to the structure of the output, not the structure of the input. Don't try to do things as you encounter information in the input; do them when you need to generate the relevant piece of the output.
There are cases when this can seem inefficient because it means visiting the same input more than once. Usually these inefficiences will prove apparent rather than real. But the first thing is to get the transformation working; if it's not fast enough you can come back to us with another question.

Formatted HTML as output from method invocation from MX4J HTTP page

I have a huge set of data and want to display the data with some formatting.
This is what the method basically looks like:
#ManagedOperation(description = "return html")
#ManagedOperationParameters({#ManagedOperationParameter(name = "someVal", description = "text")})
public String returnAsHtml(String someVal)
{
return "some formatted xml";
}
Looks like XSLTProcessor can be configured to use a XSLT template. However I could not find any examples on the internet using XSLT for html transformation in the context of MX4J. Could any one provide a sample XSLT template?
In case anyone comes back to this question, two things come to mind:
1) MX4J has several default implementations of HttpCommandProcessorAdaptor. These operations are mapped from the path. For JMX operations (aka ManagedOperation in Spring parlance), MX4J uses URLs like /invoke?operation=returnAsHtml
This will be passed to the InvokeOperationCommandProcessor to create an XML document with the result being just the toString() of whatever you returned, in an attribute called 'return'. It also passes back the return type in an attribute called 'returnclass'. You can see all this if you just add &template=identity to the invoke URL.
I mention all this because one option is to implement your own 'invoke.xsl'. The one in MX4J just calls the renderobject template:
Lo and behold, you find this in mbean_attributes.xsl, with a comment showing you exactly what you need to do:
<xsl:template name="renderobject">
<xsl:param name="objectclass"/>
<xsl:param name="objectvalue"/>
<xsl:choose>
<xsl:when test="$objectclass='javax.management.ObjectName'">
<xsl:variable name="name_encoded">
<xsl:call-template name="uri-encode">
<xsl:with-param name="uri">
<xsl:value-of select="$objectvalue"/>
</xsl:with-param>
</xsl:call-template>
</xsl:variable>
<a href="/mbean?objectname={$name_encoded}">
<xsl:value-of select="$objectvalue"/>
</a>
</xsl:when>
<xsl:otherwise>
<!-- Use the following line when the result of an invocation
returns e.g. HTML or XML data
<xsl:value-of select="$objectvalue" disable-output-escaping="true" />
-->
<xsl:value-of select="$objectvalue"/>
</xsl:otherwise>
</xsl:choose>
Setting 'disable-output-escaping' to true will do the trick
2) Another option is to write your own HttpCommandProcessorAdaptor, and set it on the HttpAdapter. This could either replace the 'invoke' processor, or you could have an entirely new one.
Hope that helps
One way I figured out is to use java script in the XSL template to extract and parse the string. Make sure you test for the browser (IE vs Non IE) and use proper parser.

Categories