xslt 2.0 : using HashMap - xslt

i have a java method that returns a map to xslt.
XSLT method call -
<xsl:variable name="mapValue" select="class:returnMap()">
Java Method -
public Map<String,List<String>> returnMap(){
Map<String,List<String>> map = new HashMap<String,List<String>>();
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
map.put("a",list);
map.put("b",list);
return map
}
how do I fetch the list value of the map in XSLT 2.0?
i tried assigning the values to a variable using the below format,
<xsl:variable name="valueOfA" select="$mapValue/entry[#key='a']"/>
<xsl:variable name="valueOfB" select="$mapValue/entry[#key='b']"/>
but, i got the below exception.
XPTY0019: Required item type of first operand of '/' is node(); supplied value has item type java:java.util.LinkedHashMap
if i try to display the value of $mapValue it prints fine.

The error message shows that you are using Saxon. The rules for conversion between Java objects and XPath values depend on which processor you use, so this is relevant.
If you are using Saxon then you have access to XSLT 3.0, and to the new map/array data types, which means that you can achieve what you are trying to achieve without calling out to Java.
The specific error you are getting is because (as the message says) $mapValue is not a node, so you can't access its children using the "/" operator. If you use an XSLT 3.0 map instead then you can access an entry using, for example $mapValue?a.

Related

current-dateTime() value is not updating on page-refresh event

I set the value of an instance to current-dateTime(). I want to update this value on page-refresh event. So I have the following code:
<xf:action ev:event="page-refresh">
<xf:setvalue ref="instance('noCache')/val"><xsl:value-of select="current-dateTime()" /></xf:setvalue>
<!-- call to resource with new value-->
</xf:action>
This above code sets the instance value (dateTime) as the same which was at page-load !
Later I use this instance value to make GET request calls to a resource and pass this updated instance value as a parameter.
Note: Using current-dateTime() to pass a parameter to the resource to avoid using cached resources (since couldn't find a way to generate random numbers
Seems like the problem I feel is that the xsl variable current-dateTime() is evaluated only on page-load and the same value is used for page-refresh. Is there some other way I can get the updated dateTime on page-refresh event ?
With XSLTForms you can call Javascript functions on XPath expressions on client side, this way:
<xf:setvalue ref="instance('noCache')/val" value="getXMLDate()" ></xf:setvalue>
And write the getXMLDate() function in your HTML:
<script>
function gwtXMLDate(){
var d = new Date();
return d.toISOString();
}
</script>

XQuery to append node to existing node set

I am new to working with XQuery and XSLT. WebLogic is using XQuery 1.0.
I have a WebLogic composite and I have a collection of nodes and just want to add a node to an existing node set in an XQuery assignment. My current solution seems too heavyweight. I will be dealing with tens of thousands of records and it seems to me there should be a really easy way to just insert a node under another node and return it.
declare function local:func($svdPpl as element() (:: element(*, sv:svdPpl) ::),
$svdPsn as element() (:: element(*, sv:svdPsn) ::))
as element() (:: element(*, sv:svdPpl) ::) {
<sv:svdPpl>
{for $psn in $svdPpl return $psn} {$svdPsn}
</sv:svdPpl>
};
If I understand XQuery correctly that means millions of loops just to add records to existing node sets. I have tried using the insert node $svdPsn into $svdPpl syntax but that is causing validation errors in the WebLogic editor.
Any help is appreciated.
XQuery is a functional language and variables are immutable. It's not possible to change the value of a variable once an assignment has been made using standard XQuery.
There is an extension to XQuery that allows updates called XQuery Update Facility, but not all processors implement it. I'm not familiar with WebLogic, but it looks like some versions support it. That would be the direct route.
Failing that, a common pattern is to write a generic recursive typeswitch function that walks the tree and applies transformation logic as it generates a modified copy of the input. This is how things work implicitly in XSLT. It's unclear from your question exactly how that would be implemented, but just as an example:
declare function local:add-after(
$context as item(),
$target-name as xs:QName,
$payload as item()*
) as item()*
{
typeswitch ($context)
case element() return (
element { node-name($context) } {
$context/#*,
for $n in $context/node()
return local:add-after($n, $target-name, $payload)
},
if (node-name($context) = $target-name)
then $payload
else ())
default return $context
};
Given an XML node $xml with a value of:
<x>
<y>
<z></z>
</y>
</x>
You could use the above function local:add-after($xml, xs:QName('y'), <payload/>) to add a payload after the <y> element:
<x>
<y>
<z></z>
</y>
<payload></payload>
</x>

Saxon XSLT .Net Transformation: What to give in BaseURI when xml and xsl both are passed as strings

This is the code I have for Saxon Transformation of XSLT files which accepts xml and xslt and returns a transformed string. I can have either xsl 1.0 or 2.0 get processed through this function.
DocumentBuilder requires a BaseURI, even if I don't have any file format. I have provided "c:\\" as the BaseURI, inspite I have nothing to do with this directory.
Is there any better way to achieve this thing or write this function?
public static string SaxonTransform(string xmlContent, string xsltContent)
{
// Create a Processor instance.
Processor processor = new Processor();
// Load the source document into a DocumentBuilder
DocumentBuilder builder = processor.NewDocumentBuilder();
Uri sUri = new Uri("c:\\");
// Now set the baseUri for the builder we created.
builder.BaseUri = sUri;
// Instantiating the Build method of the DocumentBuilder class will then
// provide the proper XdmNode type for processing.
XdmNode input = builder.Build(new StringReader(xmlContent));
// Create a transformer for the stylesheet.
XsltTransformer transformer = processor.NewXsltCompiler().Compile(new StringReader(xsltContent)).Load();
// Set the root node of the source document to be the initial context node.
transformer.InitialContextNode = input;
StringWriter results = new StringWriter();
// Create a serializer.
Serializer serializer = new Serializer();
serializer.SetOutputWriter(results);
transformer.Run(serializer);
return results.ToString();
}
If you think that the base URI will never be used (because you never do anything that depends on the base URI) then the best strategy is to set a base URI that will be instantly recognizable if your assumption turns out to be wrong, for example "file:///dummy/base/uri".
Choose something that is a legal URI (C:\ is not).

XmlDocument returned by web service as XmlNode, then XPath doesn't work?

I'm trying to utilise a web service/method which accepts an XmlDocument and returns an XmlDocument. When I add a reference to this web service to my C# application, the proxy code is defined to use XmlNodes instead of XmlDocuments. This seems fairly well recognised behaviour (but confirmation and/or an explanation would be nice - see SO here).
However, if I take the returned XmlNode object and try and do a simple XPath query to "SelectNodes(XPath)", I get no nodes found - but matching nodes do exist.
If, however, I take the OuterXml of the returned XmlNode, and create myself a new XmlDocument from it, my XPath query finds the nodes I expected.
Why is that ? What is it about the returned XmlDocument/XmlNode (or conversion between) that stops the XPath query from working as expected ?
Thanks.
Edit:
The Xml looks like this:
<Results xmlns="">
<Result>
:
<Success>True</Success>
</Result>
<Result>
:
<Success>False</Success>
</Result>
</Results>
...and the XPath looks like this...
Dim xPath As String = "//Result[Success='False']"
If (xmlResults.SelectNodes(xPath).Count > 0) Then
Throw New ApplicationException("Results returned indicate a problem:- " + xmlResults.OuterXml)
End If
One alternative to creating my own XmlDocument would be to use Linq, as per:
Dim results As XDocument = XDocument.Parse(xmlResults.OuterXml)
If (results.Descendants("Success").Count(Function(node) node.Value.ToLower() = "false") > 0) Then
Throw New ApplicationException("Results returned indicate a problem:- " + xmlResults.OuterXml)
End If

How can I pass a parameter to my xslt stylesheet with Saxon under Java?

I am using Saxon 9 HE and in NetBeans 7.0.1, I get the following error when I try to send a parameter to my stylesheet:
Just to make sure, is that the good way to send a parameter so I can get it back with
<xsl:param ... /> ?
If so, how can I use it?
Thank you!
See S9APIExamples.java :
String[] fruit = {"apple", "banana", "cherry"};
QName paramName = new QName("in");
for (String s: fruit) {
StringWriter sw = new StringWriter();
out.setOutputWriter(sw);
t.setParameter(paramName, new XdmAtomicValue(s));
t.setDestination(out);
t.transform();
System.out.println(s + ": " + sw.toString());
}
From the message it seems quite obvious that you need to pass an net.sf.saxon.s9api.Qname as the first argument (not just the string "myVar").
And the second argument must be constructed as an net.sf.saxon.s9api.XdmValue.
Just to make sure, is that the good way to send a parameter so I can get it back with
<xsl:param ... /> ?
In your XSLT stylesheets (the primary one and any stylesheet module that is referenced in an xsl:import or an xsl:include directive) you must have a global (child of xsl:stylesheet) xsl:param with the same name as the string used to construct the Qname that you are passing as the first argument to setParameter().
When the setParameter() method is executed and then the transformation is invoked, the corresponding global xsl:param will have the value that was used to construct the XdmValue passed as the second argument to setParameter().