Pass param to xslt as node in apache camel - xslt

Is it possible to pass param as node not as string?
camel context:
<setHeader headerName="document_as_node">
<simple>${body}</simple>
</setHeader>
xslt:
<xsl:param name="document_as_node" />
body is a xml document, but I'm passsing it as string (I got error when I'm trying to use this param in xpath). How can I pass this as node or how can I transform it?

Hope this example will help:
from("timer:foo?period=30s")
.setBody(constant("<oldWrapTag><someTag>123</someTag></oldWrapTag>"))
.convertBodyTo(org.w3c.dom.Document.class)
.setBody(xpath("//someTag"))
.setHeader("insert", simple("body"))
.to("xslt:/xslt/test.xsl")
.to("log:body?showBody=true")
;
xslt :
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="insert"/>
<xsl:template match="/">
<xsl:element name="wrapTag">
<xsl:copy-of select="$insert"/>
</xsl:element>
</xsl:template>
Output:
2018-10-24 14:03:07,952 | INFO | 10 - timer://foo | body | 247 - org.apache.camel.camel-core - 2.16.3 | Exchange[ExchangePattern: InOnly, BodyType: String, Body: <?xml version="1.0" encoding="UTF-8"?><wrapTag><someTag>123</someTag></wrapTag>]

Related

How to check contain only characters + space and `p` using regex

I want to check to contain only characters + space and <p> nodes inside <used>.
Input:
<root>
<used><p>String 1</p></used>
<used>string 2<p>string 3</p></used>
<used>string 4</used>
<used><image>aaa.jpg</image>para</used>
The output should be:
<ans>
<abc>string 1</abc>
<abc>string 4</abc>
</ans>
Tried code:
<ans>
<abc>
<xsl:template match="root">
<xsl:choose>
<xsl: when test="getCode/matches(text(),'^[a-zA-Z0-9]+$')">
<xsl:text>text()</xsl:text>
</xsl:when>
</xsl:choose>
</xsl:template>
</abc>
</ans>
My tried code is not working as I am expecting. How can I fix this? Thank you. I am using XSLT 2.0
You can use the following XSLT-2.0 stylesheet to get the desired result:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl= "http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<!-- Handle the <root> element -->
<xsl:template match="/root">
<ans>
<xsl:apply-templates select="used" />
</ans>
</xsl:template>
<!-- Create <abc> elements for every matching element -->
<xsl:template match="used[not(*) and matches(text(),'^[\sa-zA-Z0-9]+$')] | used[not(text()) and matches(p/text(),'^[\sa-zA-Z0-9]+$')]/p">
<abc><xsl:copy-of select="text()" /></abc>
</xsl:template>
<!-- Remove all spurious text nodes -->
<xsl:template match="text()" />
</xsl:stylesheet>
Its result is
<?xml version="1.0" encoding="UTF-8"?>
<ans>
<abc>String 1</abc>
<abc>string 4</abc>
</ans>

Create new node based on id from another xml, loop

I would like to add a node based on , but the problem is the categories are in a separate file and moreover I would like to loop to find all based on .
Let me show an example:
category.xml:
<ROOT>
<GROUPITEM>
<G_ID>1368</G_ID>
<GROUP>Phone</GROUP>
<PARENT>0</PARENT>
</GROUPITEM>
<GROUPITEM>
<G_ID>1194</G_ID>
<GROUP>Apple</GROUP>
<PARENT>1368</PARENT>
</GROUPITEM>
<GROUPITEM>
<G_ID>1195</G_ID>
<GROUP>2019</GROUP>
<PARENT>1194</PARENT>
</GROUPITEM>
</ROOT>
item.xml:
<ROOT>
<SHOPITEM>
<PRODUCT_ID>96555</PRODUCT_ID>
<GROUP_ID>1195</GROUP_ID>
<PRODUCT_NAME>Apple iPhone 8 Plus</PRODUCT_NAME>
</SHOPITEM>
</ROOT>
Example output:
<ROOT>
<SHOPITEM>
<PRODUCT_ID>96555</PRODUCT_ID>
<GROUP_ID>1195</GROUP_ID>
<PRODUCT_NAME>Apple iPhone 8 Plus</PRODUCT_NAME>
<CATEGORY>Phone | Apple | 2019</CATEGORY>
</SHOPITEM>
</ROOT>
In simple words I am looking for a way to create node (in item.xml) and add here value ( from category.xml) based on (item.xml): search in category.xml for the same -> if found, add it -> search more based on (if success add separator + value) -> loop.
Try something like (untested):
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="group" match="GROUPITEM" use="G_ID" />
<xsl:template match="SHOPITEM">
<xsl:copy>
<xsl:copy-of select="*"/>
<CATEGORY>
<xsl:apply-templates select="key('group', GROUP_ID, document('category.xml'))"/>
</CATEGORY>
</xsl:copy>
</xsl:template>
<xsl:template match="GROUPITEM">
<xsl:variable name="parent" select="key('group', PARENT)" />
<xsl:if test="$parent">
<xsl:apply-templates select="$parent"/>
<xsl:text> | </xsl:text>
</xsl:if>
<xsl:value-of select="GROUP"/>
</xsl:template>
</xsl:stylesheet>

Convert XML to XML using XSL (XSLT) & API

I have the input xml as below
<node>
<id>1234</id>
<value1>DoNoChange</value1>
<value2></value2>
<value3></value3>
</node>
Now, i would use the XSL to convert the above XML to below one
<node>
<id>1234</id>
<value1>DoNoChange</value1>
<value2>NewValue2</value2>
<value2>NewValue3</value2>
</node>
Which NewValue2 and NewValue3 is the response content from an API calls such as http://example.com/api/getDataByID/1234 which will return response as
<data>
<value2>NewValue2</value2>
<value3>NewValue3</value3>
<data>
Could you please advice my how can build the XSL for it?
What I have tried so far is
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:param name="code" select="1234"/>
<xsl:variable name="endpoint" as="xs:string" select="'http://example.com/api/getDataByID/1'"/>
<!-- the http request element -->
<xsl:variable name="request">
<http-request method="get" mime-type="application/xml" content-type="application/xml">
</http-request>
</xsl:variable>
<xsl:template match="node/id">
<xsl:variable name="rest_response" select="ex:httpSend($request, $endpoint)"/>
<id><xsl:value-of select="$rest_response/data"/></id>
</xsl:template>
</xsl:stylesheet>
The logic I'm trying to do are
When I see the match of node/id
I will call API using "id" as a param to a Rest API (Get Method)
(Note: currently I don't know how to use id as param, I hardcoded so
far)
Capture the response of the API into a variable Populate variable
data to fields such as "value2", value3" (Note: This one I have no
clue how to make it)
Thanks,
If your call to the API returns an XML response, then you should be able to do something like:
XSLT 1.0
<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="/node">
<xsl:copy>
<xsl:copy-of select="id | value1"/>
<xsl:variable name="response" select="document(concat('http://example.com/api/getDataByID/', id))" />
<value2>
<xsl:value-of select="$response/data/value2"/>
</value2>
<value3>
<xsl:value-of select="$response/data/value3"/>
</value3>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Untested, because no testing environment has been provided.

Transforming xml with namespaces using XSLT

I have the following xml
<?xml version="1.0" encoding="UTF-8"?>
<typeNames xmlns="http://www.dsttechnologies.com/awd/rest/v1" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<typeName recordType="case" href="awdServer/awd/services/v1/businessareas/SAMPLEBA/types/SAMPLECASE">SAMPLECASE</typeName>
<typeName recordType="folder" href="awdServer/awd/services/v1/businessareas/SAMPLEBA/types/SAMPLEFLD">SAMPLEFLD</typeName>
<typeName recordType="source" href="awdServer/awd/services/v1/businessareas/SAMPLEBA/types/SAMPLEST">SAMPLEST</typeName>
<typeName recordType="transaction" href="awdServer/awd/services/v1/businessareas/SAMPLEBA/types/SAMPLEWT">SAMPLEWT</typeName>
</typeNames>
I want to transform above xml as below by using XSLT:
<response>
<results>
<source>
SAMPLEST
</source>
</results>
</response>
</xsl:template>
I just want to get the source from the input xml to the output xml.
I am trying with the following xml, but couldn't get the required output xml:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:v="http://www.dsttechnologies.com/awd/rest/v1" version="2.0" exclude-result-prefixes="v">
<xsl:output method="xml" version="1.0" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="typeNames">
<response>
<results>
<source>
<xsl:value-of select="source" />
</source>
</results>
</response>
</xsl:template>
</xsl:stylesheet>
I. Namespace in input xml
<typeNames xmlns="http://www.dsttechnologies.com/awd/rest/v1"...
xmlns puts self + all child nodes into a namespace. This namespace does not need any prefix.
II. Namespace in XSLT
... xmlns:v="http://www.dsttechnologies.com/awd/rest/v1"...
You prefixed the namespace (same uri as source) with v, so you have to write this prefix in your xpath as well.
<xsl:template match="v:typeNames">
[XSLT 2.0: you also can add xpath-default-namespace="uri" in the stylesheet section, to define a default namespace for all xpath-expressions. Therefore you dont have to prefix the namespace.]
III. Guessing on given input xml
<xsl:value-of select="source" /> -> <typeName recordType="source"..>SAMPLEST</typeName>
If you want to select the shown xml-node, you have to write one of the following:
absolute, without any context node:
/v:typeNames/v:typeName[#recordType = 'source']
on context-node typeNames:
v:typeName[#recordType = 'source']
[<xsl:value-of select="..."/> will return the text-node(s), e.g. "SAMPLEST"]
EDIT:
What if there are two tags.
First things first: <xsl:value-of in XSLT 1 can only work with 1 node! If the xpath expression matches more than one node, it will just process the first one!
Solve it like this way:
...
<results>
<xsl:apply-templates select="v:typeName[#recordType = 'source']"/>
</results>
...
<xsl:template match="v:typeName[#recordType = 'source']">
<source>
<xsl:value-of select="."/>
</source>
</xsl:template>
The apply-templates within results searches for all typeName..source. The matching template listens to that node and creates the xml <source>....

Coying an entire xml in a Variable using xslt

How can i copy an entire xml as is in an Variable?
Below is the sample xml:
<?xml version="1.0" encoding="UTF-8"?>
<products author="Jesper">
<product id="p1">
<name>Delta</name>
<price>800</price>
<stock>4</stock>
</product>
</products>
I have tried below xslt but it is not working.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:variable name="reqMsg">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:variable>
<xsl:copy-of select="$reqMsg"/>
</xsl:template>
</xsl:stylesheet>
Regards,
Rahul
Your transformation fails because at a certain point, it tries to create a variable (result tree fragment) containing an attribute node. This is not allowed.
It's not really clear what you mean by "copying an entire XML to a variable". But you probably want to simply use the select attribute on the root node:
<xsl:variable name="reqMsg" select="/"/>
This will actually create variable with a node-set containing the root node of the document. Using this variable with xsl:copy-of will output the whole document.
<xsl:copy-of select="document('path/to/file.xml')" />
Or if you need it more than once, to avoid repeating the doc name:
<xsl:variable name="filepath" select="'path/to/file.xml'" />
…
<xsl:copy-of select="document($filepath)" />
The result of document() should be cached IIRC, so don't worry about calling it repeatedly.