What is the recommended approach to use Mule choice router with XSLT - xslt

I am using the below shown snippet of choice element in my Mule 3.3 flow. XSL Transformer feeds the choice element. XSL Transformer is supposed to return a String (name of an entity) and on the basis of string value, I use choice router to push it to different jms queues.
<flow name="ProcessOrder">
.
.
<xm:xslt-transformer xsl-file="xsl/getEntity.xslt" returnClass="java.lang.String"/>
<choice>
<when expression="payload.contains('ABC')">
<jms:outbound-endpoint queue="order.queue1" />
</when>
<when>
</when>
<otherwise>
</otherwise>
</choice>
</flow>
XSL Transformer returns this payload
<?xml version="1.0" encoding="UTF-8"?>ABC
My question is how do I compare the String returned. I don't think payload.contains() is the best way to do this, though it solves my purpose and also we won't have matching entities returned which are ever like ABCxy but still this is not a full proof solution.

Add the omit-xml-declaration part in your xslt as shown below. This will give you the raw string without the prolog.
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:template match="/">
...
...
This will give
"ABC" as output instead of "<?xml version="1.0" encoding="UTF-8"?>ABC"
Then in the expression use it like
<when expression="#[message.payload.contains('ABC')]">
This way it should work.

Maybe this is what you are looking for:
<when evaluator="xpath" expression="/result/" ...
Obviously, your XSLT will need to retur a well-formed XML document with the desired result in an XML element that is neatly accessible by XPath.

On Mule website they suggest to use expression-splitter-router evaluator this is an example from mule website of how to use it:
FruitBowl containing an apple, an orange, and two bananas. When Mule receives this object, we want to route the fruit to different locations: the AppleService, BananaService, and OrangeService.
<service name="Distributor">
<inbound>
<jms:inbound-endpoint queue="distributor.queue"/>
</inbound>
<outbound>
<!-- FruitBowl.getFruit() List -->
<expression-splitter-router evaluator="bean" expression="fruit">
<vm:outbound-endpoint path="apple.service.queue">
<payload-type-filter expectedType="org.mule.tck.testmodels.fruit.Apple"/>
</vm:outbound-endpoint>
<vm:outbound-endpoint path="banana.service.queue">
<payload-type-filter expectedType="org.mule.tck.testmodels.fruit.Banana"/>
</vm:outbound-endpoint>
<vm:outbound-endpoint path="orange.service.queue">
<payload-type-filter expectedType="org.mule.tck.testmodels.fruit.Orange"/>
</vm:outbound-endpoint>
</expression-splitter-router>
</outbound>
</service>
Hope that helps

Related

Processing hl7 type message using xslt or regex, or combination of two (XSLT 1.0)

so I have this hl7 type message that I have to transform using either regex or xslt or combination of two.
Format of this message is DateTime(as in YYYYMMDDHHMMSS)^UnitName^room^bed|). Each location is separated with a pipe, so each person can have one or multiple locations.
And the messages looks like this( when a patient has only one location):
20130602201605^Some Hospital^ABFG^411|
End xml result should look like this:
<Location>
<item>
<when>20130602201605</when>
<UnitName>Some Hospital</UnitName>
<room>ABFG</room>
<bed>411</bed>
</item>
</Location>
I would probably use substring type of function if it was only one location.
The problem I am running into is when there is more than one. I am relatively new to xslt and regex in general so I don't know how to use recursion in these instances.
So if I have a message like this with multiple locations:
20130601003203^GBMC^XXYZ^110|20130602130600^Sanai^ABC^|20130602150003^John Hopkins^J615^A|
The end result should be:
<Location>
<item>
<when>0130601003203</when>
<UnitName>GBMC</UnitName>
<room>XXYZ</room>
<bed>110</bed>
</item>
<item>
<when>20130602130600</when>
<UnitName>Sanai</UnitName>
<room>ABC</room>
<bed></bed>
</item>
<item>
<when>20130602150003</when>
<UnitName>John Hopkins</UnitName>
<room>J615</room>
<bed>A</bed>
</item>
</Location>
So how would I solve this? Thanks in advance.
Given that your Hl7 message is "|^~\&" encoded and not in an XML format, it is not clear how you will be using an XSLT 1.0 processor for your task. Can you describe your processing pipeline in greater detail? Your snippets are not complete messages, and it is not clear whether you will be starting with complete messages or attempting to parse isolated fields handed to a larger processing task through parameters or something.
If your processing starts with a complete HL7 message, I would suggest looking into the HAPI project, or a similar set of libraries, to have the messages converted from |^~\& to </> format, then invoking your XSLT on that version of the data. (You could also use the HAPI libraries in a full-Java solution. In either case, there are code examples at the HAPI site and at an Apache site on HL7.) If you are not interested in using Java at all, but are open to partial non-XSLT solutions, there are other projects that provide similar serialization options (e.g., Net::HL7 for Perl, nHAPI for VB/C#, etc.).
If you have isolated "|^~\&" encoded data in an otherwise XML formatted file, then I would suggest looking into the str:tokenize function in the XSLT 1.0 exslt functions. (XSLT 2.0 has a built-in tokenize function.) You can have str:tokenize split your data on the field or component separators, then create elements using the tokenized substrings.
Here is a stylesheet
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:str="http://exslt.org/strings"
extension-element-prefixes="str"
version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="data">
<Location>
<xsl:for-each select="str:tokenize(.,'|')">
<xsl:call-template name="handle-field">
<xsl:with-param name="field" select="."/>
</xsl:call-template>
</xsl:for-each>
</Location>
</xsl:template>
<xsl:template name="handle-field">
<xsl:param name="field"/>
<xsl:variable name="components" select="str:tokenize($field,'^')"/>
<item>
<when><xsl:value-of select="$components[1]"/></when>
<UnitName><xsl:value-of select="$components[2]"/></UnitName>
<room><xsl:value-of select="$components[3]"/></room>
<bed><xsl:value-of select="$components[4]"/></bed>
</item>
</xsl:template>
</xsl:stylesheet>
that runs over this input
<?xml version="1.0" encoding="UTF-8"?>
<data>20130601003203^GBMC^XXYZ^110|20130602130600^Sanai^ABC^|20130602150003^John Hopkins^J615^A|</data>
to produce this output with xsltproc:
<?xml version="1.0"?>
<Location>
<item>
<when>20130601003203</when>
<UnitName>GBMC</UnitName>
<room>XXYZ</room>
<bed>110</bed>
</item>
<item>
<when>20130602130600</when>
<UnitName>Sanai</UnitName>
<room>ABC</room>
<bed/>
</item>
<item>
<when>20130602150003</when>
<UnitName>John Hopkins</UnitName>
<room>J615</room>
<bed>A</bed>
</item>
</Location>
Your source message is in a string form, you need to create a parser that uses regex to split the message based on first pipes and then carat. refer to Unable to parse ^ character which has my original code for the parser and the solution gives a different approach to it.
After you have individual elements you need to add it to your xml as nodes.

BaseX: where to declare the XML document on which to perform a query

With the program BaseX I was able to use XPath and XQuery in order to query an XML document located at my home directory, but I have a problem with doing the same in XSLT.
The document I'm querying is BookstoreQ.xml.
XPath version, running totally fine:
doc("/home/ioannis/Desktop/BookstoreQ.xml")/Bookstore/Book/Title
XSLT code which I want to execute:
<xsl:stylesheet version = "2.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
<xsl:output method= "xml" indent = "yes" omit-xml-declaration = "yes" />
<xsl:template match = "Book"></xsl:template>
</xsl:stylesheet>
I read BaseX' documentation on XSLT, but didn't manage to find a solution. How can I run given XSLT?
BaseX has no direct support for XSLT, you have to call it using XQuery functions (which is easy, though). There are two functions for doing this, one for returning XML nodes (xslt:transform(...)), one for returning text as a string (xslt:transform-text(...)). You need the second one.
xslt:transform-text(doc("/home/ioannis/Desktop/BookstoreQ.xml"),
<xsl:stylesheet version = "2.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
<xsl:output method= "xml" indent = "yes" omit-xml-declaration = "yes" />
<xsl:template match = "Book"></xsl:template>
</xsl:stylesheet>
)
Both can either be called with the XSLT as nodes (used here), by passing it as a string or giving a path to a file containing the XSLT code.

XSLT transformation passing parameters

I am trying to pass parameters during an XSLT transformation. Here is the xsl stylesheet.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="param1" select="'defaultval1'" />
<xsl:param name="param2" select="'defaultval2'" />
<xsl:template match="/">
<xslttest>
<tagg param1="{$param1}"><xsl:value-of select="$param2" /></tagg>
</xslttest>
</xsl:template>
</xsl:stylesheet>
The following in the java code.
File xsltFile = new File("template.xsl");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document stylesheet = builder.parse("template.xsl");
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer xsltTransformer = transformerFactory.newTransformer(new DOMSource(stylesheet));
//Transformer xsltTransformer = transformerFactory.newTransformer(new StreamSource(xsltFile));
xsltTransformer.setParameter("param1", "value1");
xsltTransformer.setParameter("param2", "value2");
StreamResult result = new StreamResult(System.out);
xsltTransformer.transform(new DOMSource(builder.newDocument()), result);
I get following errors:-
ERROR: 'Variable or parameter 'param1' is undefined.'
FATAL ERROR: 'Could not compile stylesheet'
However, if i use the following line to create the transformer everything works fine.
Transformer xsltTransformer = transformerFactory.newTransformer(new StreamSource(xsltFile));
Q1. I just wanted to know whats wrong in using a DOMSource in creating a Transformer.
Q2. Is this one of the ideal ways to substitute values for placeholders in an xml document? If my placeholders were in a source xml document is there any (straightforward) way to substitute them using style sheets (and passing parameters)?
Q1: This is a namespace awareness problem. You need to make the DocumentBuilderFactory namespace aware:
factory.setNamespaceAware(true);
Q2: There are several ways to get the values from an external xml file. One way to do this is with the document function and a top level variable in the document:
<!-- Loads a map relative to the template. -->
<xsl:variable name="map" select="document('map.xml')"/>
Then you can select the values out of the map. For instance, if map.xml was defined as:
<?xml version="1.0" encoding="UTF-8"?>
<mappings>
<mapping key="value1">value2</mapping>
</mappings>
You could remove the second parameter from your template, then look up the value using this line:
<tagg param1="{$param1}"><xsl:value-of select="$map/mappings/mapping[#key=$param1]"/></tagg>
Be aware that using relative document URIs will require that the stylesheet has a system id specified, so you will need to update the way you create your DOMSource:
DOMSource source = new DOMSource();
source.setNode(stylesheet);
source.setSystemId(xsltFile.toURL().toString());
In general, I suggest looking at all of the options that are available in Java's XML APIs. Assume that all of the features available are set wrong for what you are trying to do. I also suggest reading the XML Information Set. That specification will give you all of the definitions that the API authors are using.

XSLT - Two seperate data sources merged into one XSLT

I've have two XML data sources which are completly seperate. UserDetails.xml and UserSites.xml.
The UserDetails.xml contains:
<a:UserDetails>
<a:user>
<a:username>Clow</a:username>
<a:userid>9834</a:userid>
</a:user>
<a:user>
<a:username>Adam</a:username>
<a:userid>9867</a:userid>
</a:user>
</a:UserDetails>
UserSites.xml contains:
<a:UserSites>
<a:site>
<a:createdby>9834</a:userid>
<a:type>blog</a:type>
</a:site>
<a:site>
<a:createdby>9867</a:username>
<a:type>web</a:type>
</a:site>
What I would like to do is use data in both of these data sources to indicate which users have sites created and what type of site they have.
How can this be made possible in XSLT 1.0?
Use the document function to access nodes in an external document
For example, the following stylesheet applied to UserDetails.xml:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:a="a">
<xsl:template match="/">
<test>
<xsl:value-of
select="document('UserSites.xml')/a:UserSites/a:site/a:createdby"/>
</test>
</xsl:template>
</xsl:stylesheet>
Outputs the following result from UserSites.xml:
9834
Note: Your example XML is not well-formed, so I had to make minor adjustments before processing.

Xslt transform on special characters

I have an XML document that needs to pass text inside an element with an '&' in it.
This is called from .NET to a Web Service and comes over the wire with the correct encoding &
e.g.
T&O
I then need to use XSLT to create a transform but need to query SQL server through a SP without the encoding on the Ampersand e.g T&O would go to the DB.
(Note this all has to be done through XSLT, I do have the choice to use .NET encoding at this point)
Anyone have any idea how to do this from XSLT?
Note my XSLT knowledge isn’t the best to say the least!
Cheers
<xsl:text disable-output-escaping="yes">&<!--&--></xsl:text>
More info at: http://www.w3schools.com/xsl/el_text.asp
If you have the choice to use .NET you can convert between an HTML-encoded and regular string using (this code requires a reference to System.Web):
string htmlEncodedText = System.Web.HttpUtility.HtmlEncode("T&O");
string text = System.Web.HttpUtility.HtmlDecode(htmlEncodedText);
Update
Since you need to do this in plain XSLT you can use xsl:value-of to decode the HTML encoding:
<xsl:variable name="test">
<xsl:value-of select="'T&O'"/>
</xsl:variable>
The variable string($test) will have the value T&O. You can pass this variable as an argument to your extension function then.
Supposing your XML looks like this:
<root>T&O</root>
you can use this XSLT snippet to get the text out of it:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:template match="root"> <!-- Select the root element... -->
<xsl:value-of select="." /> <!-- ...and extract all text from it -->
</xsl:template>
</xsl:stylesheet>
Output (from Saxon 9, that is):
T&O
The point is the <xsl:output/> element. The defauklt would be to output XML, where the ampersand would still be encoded.