Using Smooks or Groovy with Java Camel to split/transform XML - groovy

Camel version 2.14 Smooks version 1.5.1
I got a message which i want to split and transform, but i need the id from the parent. So I thought about using Smooks, splitting the message, transforming and send each output to a queue. Which will be using freemarker template for the transform.
<!-- Message -->
<data>
<id>123</id> <!-- This is needed in both portal messages -->
<portals>
<portal id="1" />
<portal id="2" />
</portals
</data>
<!-- Msg 1 -->
<portal dataId="123">
<id>1</id>
<portal>
<!-- Msg 2 -->
<portal dataId="123">
<id>2</id>
<portal>
There are plenty of examples. But for example the camel examples does not work, due to "java.lang.ClassNotFoundException: org.apache.camel.component.ResourceBasedComponent" which is a known issue.
An alternative would be using groovy for transformation?
So, how could this easiest be solved?

I don't know about smooks, but you can combine the XSLT transformer with a XPATH splitter to do this.
First, transform the data into the blocks that should make up each message. Do it using XSLT, groovy or whatever you feel comfortable with. Here is a simple stylesheet, to be put into src/main/resources (or any classpath location).
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<portals>
<xsl:variable name="dataId" select="/data/id"/>
<xsl:for-each select="/data/portals/portal">
<portal dataId="$dataId">
<xsl:attribute name="dataId">
<xsl:value-of select="/data/id"/>
</xsl:attribute>
<id><xsl:value-of select="#id"/></id>
</portal>
</xsl:for-each>
</portals>
</xsl:template>
The Camel route: First the transform, then splitter. The "to" can be whatever, like a seda/direct for further processing or the target protocol.
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="file:data"/>
<to uri="xslt:transform.xslt"/>
<split>
<xpath>portals/portal</xpath>
<to uri="log:foo.bar?level=INFO"/>
</split>
</route>

In groovy it can be done this way:
import groovy.util.XmlSlurper
import groovy.xml.MarkupBuilder
def xml = """
<data>
<id>123</id>
<portals>
<portal id="1" />
<portal id="2" />
</portals>
</data>
"""
def slurped = new XmlSlurper().parseText(xml)
def msgId = slurped.id
def portalIds = slurped.portals.portal.#id*.text()
def portalXmls = portalIds.collect { portalId ->
writer = new StringWriter()
portalXml = new MarkupBuilder(writer)
portalXml.doubleQuotes = true
portalXml.portal(dataId: msgId) {
id(portalId)
}
writer
}.each { println it.toString() }
null

Related

SaxonJS - Prevent Server Reloading When Stylesheet Changes

I am using Saxon-JS to convert an XML file to an XSL-fo. I follow the documentation and leverage the xslt3 command to first compile an XSL file to a sef file such as:
"compile": "xslt3 -xsl:./src/styles/sample.xsl -export:sef/sample.sef.json -t -nogo -ns:##html5"
However, this causes an issue that the server needs to reload and compile the stylesheet whenever there is a change in stylesheet.
My question is: how to prevent the server from reloading when a change is made? Thank you in advance.
Using SaxonJS.XPath.evaluate('transform(...)', ..., { ... }) it is also possible to run XSLT directly. Whether that works and performs for you is something you would need to test, I don't know how complex your XSLT is, whether it has includes or imports, how large the input XML and how large the resulting XSL-FO is. But if the target format is XSL-FO and not HTML in the browser (although that way I don't understand the use of -ns:##html5) you might not need the interactive Saxon extensions that the xslt3 compilation supports and adds.
Simple example:
const SaxonJS = require("saxon-js")
const xml = `<root>
<item>a</item>
<item>b</item>
</root>`;
const xslt = `<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
exclude-result-prefixes="xs"
expand-text="yes">
<xsl:output method="xml" indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="/" name="xsl:initial-template">
<fo:root>
<fo:layout-master-set>
<fo:simple-page-master master-name="sample">
<fo:region-body/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="sample">
<fo:flow flow-name="xsl-region-body">
<fo:block>
<xsl:apply-templates/>
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
<xsl:template match="root">
<fo:list-block>
<xsl:apply-templates/>
</fo:list-block>
</xsl:template>
<xsl:template match="item">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block>
<xsl:number format="1."/>
</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:apply-templates/>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</xsl:template>
</xsl:stylesheet>`;
const result = SaxonJS.XPath.evaluate(
`transform(
map {
'stylesheet-text' : $xslt,
'source-node' : parse-xml($xml),
'delivery-format' : 'serialized'
}
)?output`,
[],
{ params : { 'xml' : xml, 'xslt' : xslt } }
);
console.log(result);

Groovy get xml tags attribute value

I am trying to get the value of the attribute IntegratorCode AND ProductType using groovy.
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAPNYK1="http://www.w3.org/2001/XMLSchema"
xmlns:SOAPNYK2="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAPNYK3="http://schemas.xmlsoap.org/soap/encoding/">
<soap:Header>
<Authentication
xmlns="urn:iQQ:API:22:iQQMessages.xsd" EmployeeCode="*****" IntegratorCode="GENERIC" Pwd="******" Usr="SYSUSER"/>
</soap:Header>
<soap:Body>
<CanOfferProduct
xmlns="urn:iQQ:API:22:iQQMessages.xsd">
<OnQuotePackage
xmlns="" ProductType="GAP" QuoteNumber="74859">test
</OnQuotePackage>/>
</CanOfferProduct>
</soap:Body>
</soap:Envelope>
I tried the following method using XmlSlurpur
def result = new XmlSlurper().parseText(xml)
println(result.Envelope.Header[1].Authentication.#IntegratorCode)
//output = IntegratorCode
I wanted to print the value of IntegratorCode which is "GENERIC".
I am not sure what I am doing wrong in this case. please help.
what you called result is soap:Envelope, so you essentially had Envelope.Envelope... This works:
println result.Header.Authentication.#IntegratorCode
As a precaution I normally use it like this:
def Envelope = new XmlSlurper().parseText(xml)

Change xml element/tag name using XmlSlurper or XmlParser

I have an xml that looks like this
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Samples>
<Sample>
<Name>
Sample1
</Name>
<Date>
01/20/2016
</Date>
</Sample>
</Samples>
I want to simply change the tag name from "Samples" to "SampleList". How will I do it?
replaceNode can be used to rename the node as below:
def xml = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Samples>
<Sample>
<Name>
Sample1
</Name>
<Date>
01/20/2016
</Date>
</Sample>
</Samples>
'''
def result = new XmlSlurper().parseText(xml)
result.replaceNode {
'SampleList' it.children()
}
groovy.xml.XmlUtil.serialize(result)
replaceNode takes a closure as method parameter which delegates to a builder. Specifically in this case the node is replaced instead of appending it to main document. 'SampleList' it.children() is similar to 'SampleList(it.children())'.
Parsed xml's root element being Samples (which needs replacement), replaceNode was directly done on the result.

Referencing an external link from DITA map

Is there any way to reference an external document from DITA?
According to some guidelines it should be easy via (within <map>) <keydef> keys=keyID and (within <task>) <link>keyref=keyID,
but the validator simply does not accept it.
I intend to reference an xlsx-sheet:
<title>SpringerMaterials Content Enrichment - Annotation Guide - V01.00</title>
<keydef keys="protocolFile" href="examples/VIII6A3_CV_prototype.xlsx" format="xlsx" scope="external"/>
If the keydef you show is in a map then it looks correct.
Within a topic you can use to point to the key:
<p>See <xref keyref="protocolFile">Sample Protocol Data</xref>...
What validation message are you getting?
It seems your map is not valid. Hopefully this example helps you.
myMap.ditamap
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE map PUBLIC "-//OASIS//DTD DITA Map//EN" "map.dtd">
<map>
<title>Title of Your Map</title>
<!-- Reference to the Topic -->
<topicref keys="myTopic"/>
<!-- Your Keymap -->
<mapref href="myKeymap.ditamap" format="ditamap"/>
</map>
myKeymap.ditamap
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE map PUBLIC "-//OASIS//DTD DITA Map//EN" "map.dtd">
<map>
<title>My Keymap</title>
<keydef keys="myTopic" href="myTopic.dita"/>
<keydef keys="myFile" href="myFile.xlsx" navtitle="myFile" scope="external" format="xlsx"/>
</map>
myTopic.dita
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE topic PUBLIC "-//OASIS//DTD DITA Topic//EN" "topic.dtd">
<topic id="myTopic">
<title>myTopic</title>
<body>
<p>This is a link to myFile.xlsx: <xref keyref="myFile"/>.</p>
</body>
</topic>

Cannot find a script or an extension object associated with namespace http://schemas.microsoft.com/WebParts/v3/Publishing/runtime

I have a custom Xsl file to use with my XsltListViewWebPart, but when i run the page with the web part inside, i get the following exception in my log:
Error while executing web part: System.Xml.Xsl.XslTransformException: Cannot find a script or an extension object associated with namespace 'http://schemas.microsoft.com/WebParts/v3/Publishing/runtime'. at System.Xml.Xsl.Runtime.XmlQueryContext.InvokeXsltLateBoundFunction(String name, String namespaceUri, IList`1[] args) at <xsl:template name="OuterTemplate.GetSafeStaticUrl">(XmlQueryRuntime {urn:schemas-microsoft-com:xslt-debug}runtime, XPathNavigator {urn:schemas-microsoft-com:xslt-debug}current, String UrlColumnName) at <xsl:template match="Row">(XmlQueryRuntime {urn:schemas-microsoft-com:xslt-debug}runtime, XPathNavigator {urn:schemas-microsoft-com:xslt-debug}current) at <xsl:template match="dsQueryResponse">(XmlQueryRuntime {urn:schemas-microsoft-com:xslt-debug}runtime... 1fbf809c-8c95-d00a-8f55-21bd43c1d0d2
89a1 High ..., XPathNavigator {urn:schemas-microsoft-com:xslt-debug}current) at <xsl:apply-templates>(XmlQueryRuntime {urn:schemas-microsoft-com:xslt-debug}runtime, XPathNavigator ) at Root(XmlQueryRuntime {urn:schemas-microsoft-com:xslt-debug}runtime) at System.Xml.Xsl.XmlILCommand.Execute(Object defaultDocument, XmlResolver dataSources, XsltArgumentList argumentList, XmlWriter writer) at Microsoft.SharePoint.WebPartPages.DataFormWebPart.ApplyXslTransform(XPathNavigator dataNavigator, XslCompiledTransform xslCompiledTransform, XsltArgumentList xmlArguments) at Microsoft.SharePoint.WebPartPages.DataFormWebPart.ExecuteTransform(XslCompiledTransform xslCompiledTransform, XsltArgumentList xmlArguments, Boolean bDeferExecuteTransform) at Microsoft.SharePoint.WebPartPages.DataFormWeb... 1fbf809c-
My xsl file is:
<xsl:stylesheet version="1.0"
exclude-result-prefixes="msxsl"
xmlns:x="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:cmswrt="http://schemas.microsoft.com/WebParts/v3/Publishing/runtime"
xmlns:cbq="urn:schemas-microsoft-com:ContentByQueryWebPart"
xmlns:d="http://schemas.microsoft.com/sharepoint/dsp"
xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime"
xmlns:ddwrt2="urn:frontpage:internal"
xmlns:msxsl="urn:schemas-microsoft-com:xslt">
...
<xsl:template name="OuterTemplate.GetSafeStaticUrl">
<xsl:param name="UrlColumnName"/>
<xsl:variable name="Url">
<xsl:call-template name="OuterTemplate.FormatColumnIntoUrl">
<xsl:with-param name="UrlColumnName" select="$UrlColumnName"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="cmswrt:EnsureIsAllowedProtocol($Url)"/> // Using cmswrt here
</xsl:template>
...
</xsl:stylesheet>
What i did wrong?
When using the Custom XSLT option, you have to maintain the Extension Xml yourself, just as you would for inline custom Xslt that calls external Assemblies.
Docs: http://msdn.microsoft.com/en-us/library/aa547368.aspx
Sample: http://blog.vertica.dk/2013/03/20/using-custom-xslt-in-biztalk/

Resources