How to remove most namespaces from input XML in XSLT (nokogiri example) - ruby

I have this piece of nokogiri code, thats runs slower then I like on large input. How would you redo this in XSLT? Any other ideas to make it run faster?
# remove namespaces (other then soapenv) from input xml, and move
# them to type attribute.
# xml is Nokogiri::XML object
def cleanup_namespaces(xml)
protected_ns = %w( soapenv )
xml.traverse do |el|
next unless el.respond_to? :namespace
if (ns=el.namespace) &&
!protected_ns.include?(ns.prefix) then
el['type'] = "#{ns.prefix}:#{el.name}"
el.namespace = nil
end
end
xml
end
The sample input I am testing with is:
<?xml version="1.0"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<ns1:getAccountDTOResponse xmlns:ns1="http://www.example.com/pw/services/PWServices"
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<getAccountDTOReturn xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns2="urn:PWServices"
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xsi:type="ns2:Account">
<ns1:ID soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xsi:type="xsd:long">0</ns1:ID>
<ns1:accountNumber xsi:type="soapenc:string" />
<ns1:accountType xsi:type="soapenc:string" />
<ns1:clientData xsi:type="soapenc:Array" xsi:nil="true" />
<ns1:name xsi:type="soapenc:string" />
<ns1:parentRef xsi:type="soapenc:string" />
</getAccountDTOReturn>
</ns1:getAccountDTOResponse>
</soapenv:Body>
</soapenv:Envelope>
The expected output is:
<?xml version="1.0"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<getAccountDTOResponse xmlns:ns1="http://www.example.com/pw/services/PWServices"
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
type="ns1:getAccountDTOResponse">
<getAccountDTOReturn xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns2="urn:PWServices"
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xsi:type="ns2:Account">
<ID soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xsi:type="xsd:long" type="ns1:ID">0</ID>
<accountNumber xsi:type="soapenc:string"
type="ns1:accountNumber" />
<accountType xsi:type="soapenc:string"
type="ns1:accountType" />
<clientData xsi:type="soapenc:Array" xsi:nil="true"
type="ns1:clientData" />
<name xsi:type="soapenc:string" type="ns1:name" />
<parentRef xsi:type="soapenc:string"
type="ns1:parentRef" />
</getAccountDTOReturn>
</getAccountDTOResponse>
</soapenv:Body>
</soapenv:Envelope>
This input is a SOAP response. A tangential question is, what is the
point of the ns1 type namespace in the SOAP response, and is it
reasonable to throw them away completely. I don't seem to need to
reference them when parsing the response.

This XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#* | node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="*[namespace-uri() =
'http://www.example.com/pw/services/PWServices']">
<xsl:element name="{local-name()}">
<xsl:attribute name="type">
<xsl:value-of select="name()"/>
</xsl:attribute>
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Against your sample will produce this result:
<?xml version="1.0" encoding="UTF-16"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<getAccountDTOResponse type="ns1:getAccountDTOResponse" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<getAccountDTOReturn soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns2:Account" xmlns:ns1="http://www.example.com/pw/services/PWServices" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns2="urn:PWServices">
<ID type="ns1:ID" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="xsd:long">0</ID>
<accountNumber type="ns1:accountNumber" xsi:type="soapenc:string"></accountNumber>
<accountType type="ns1:accountType" xsi:type="soapenc:string"></accountType>
<clientData type="ns1:clientData" xsi:type="soapenc:Array" xsi:nil="true"></clientData>
<name type="ns1:name" xsi:type="soapenc:string"></name>
<parentRef type="ns1:parentRef" xsi:type="soapenc:string"></parentRef>
</getAccountDTOReturn>
</getAccountDTOResponse>
</soapenv:Body>
</soapenv:Envelope>

Related

XPath preceding depth-first tree element

I have a tree structure:
<pages>
<page id="1">
<page id="1A" />
<page id="1B" />
</page>
<page id="2" />
<page id="3">
<page id="3A" />
<page id="3B" />
<page id="3C" />
</page>
</pages>
I want to emit "previous" links with XSLT. I have succeeded in creating "next" links.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/pages">
<pages>
<xsl:for-each select=".//page">
<page id="{#id}" prev="???" next="{(child::page|following::page)/#id}" />
</xsl:for-each>
</pages>
</xsl:template>
</xsl:stylesheet>
Expected output:
$ xsltproc test.xsl test.xml
<?xml version="1.0"?>
<pages>
<page id="1" prev="" next="1A"/>
<page id="1A" prev="1" next="1B"/>
<page id="1B" prev="1A" next="2"/>
<page id="2" prev="1B" next="3"/>
<page id="3" prev="2" next="3A"/>
<page id="3A" prev="3" next="3B"/>
<page id="3B" prev="3A" next="3C"/>
<page id="3C" prev="3B" next=""/>
</pages>
I think
<page id="{#id}" prev="{(ancestor::page[1]/#id|preceding::page[1]/#id)[last()]}" next="{(child::page|following::page)/#id}" />
is what you are looking for.
Another way you could look at it:
XSLT 1.0 (with a node-set() extension function)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/pages">
<xsl:variable name="all-pages">
<xsl:copy-of select=".//page"/>
</xsl:variable>
<pages>
<xsl:for-each select="exsl:node-set($all-pages)/page">
<page id="{#id}" prev="{preceding-sibling::page[1]/#id}" next="{following-sibling::page[1]/#id}" />
</xsl:for-each>
</pages>
</xsl:template>
</xsl:stylesheet>

Is there a way to conditionally sort in xslt?

I need to sort some content but only when an attribute is equal to CAT. I think I should be able to pass a property from my ant build file to the use-when attribute but it is not working. Any help would be appreciated
Here is the xslt that I have:
<xsl:for-each select="document(#fileRef)/foo/bar">
<xsl:sort select="translate(child::Title/text(), '>', '')" order="ascending" use-when="system-property('customerCode')='CAT'"
collation="http://www.w3.org/2005/xpath-functions/collation/html-ascii-case-insensitive"/>
<!-- do some stuff here -->
</xsl:for-each>
Using oXygen I got the following to work in an Ant file:
<xslt in="sample1.xml" out="sample1-transformed.xml" force="true" style="sheet1.xsl">
<factory name="net.sf.saxon.TransformerFactoryImpl"/>
<sysproperty key="cat" value="bar"/>
</xslt>
XML sample1.xml is e.g.
<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>c</item>
<item>a</item>
</root>
XSLT is
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:comment select="system-property('cat')"/>
<xsl:next-match/>
</xsl:template>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates select="item">
<xsl:sort select="." use-when="system-property('cat') = 'foo'"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
and then the output items are sorted only if the Ant sets e.g. <sysproperty key="cat" value="foo"/>.

XSLT 2.0 Sort by variable from another style sheet

I need to create XML that is sorted by numeric values I pull from another XSLT, which I use as a cross reference. The below source XML (source.xml) has four alpha characters at Partner/Header/#whse.
<?xml version="1.0" encoding="UTF-8"?>
<Partner partnerId="TradingPartner1">
<Header whse="NCCH" >
<Contract claimNumber="00000000" />
</Header>
<Header whse="TXAU" >
<Contract claimNumber="00000000" />
</Header>
<Header whse="LANO" >
<Contract claimNumber="00000000" />
</Header>
<Header whse="MIGR">
<Contract claimNumber="00000000" />
</Header>
<Header whse="TXHO">
<Contract claimNumber="00000000" />
</Header>
</Partner>
I need to cross reference the alpha characters to get the DUNS+4.
I use this XSLT (Duns_config.xslt) to get the DUNS.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template name="SHIPTODUNS">
<xsl:param name="Whse" />
<xsl:choose>
<xsl:when test="$Whse = 'LANO'"><xsl:value-of select="'0044893600101'" /></xsl:when>
<xsl:when test="$Whse = 'TXHO'"><xsl:value-of select="'0044893600103'" /></xsl:when>
<xsl:when test="$Whse = 'TXAU'"><xsl:value-of select="'0044893600105'" /></xsl:when>
<xsl:when test="$Whse = 'NCCH'"><xsl:value-of select="'0044893600214'" /></xsl:when>
<xsl:when test="$Whse = 'MIGR'"><xsl:value-of select="'8949713340601'" /></xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
In the main XSLT (Transaction.xslt), I include the Duns_config.xslt and call SHIPTODUNS putting the data in the variable $headerDuns. I then get the last three digits of the DUNS+4 and put them into the variable $varWhse and try to sort by this variable:
<?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" encoding="UTF-8" indent="yes"/>
<xsl:include href="Duns_config.xslt"/>
<xsl:template match="Partner">
<Partner partnerId="{./#partnerId}">
<xsl:apply-templates select="./Header" />
</Partner>
</xsl:template>
<xsl:template match="Header">
<xsl:variable name="headerDuns">
<xsl:call-template name = "SHIPTODUNS">
<xsl:with-param name="Whse" select="./#whse" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="varWhse">
<xsl:value-of select="substring($headerDuns, 11, 3)" />
</xsl:variable>
<xsl:for-each select="current()">
<xsl:sort select="$varWhse" />
<transaction varwhse="{$varWhse}">
<duns number="{$headerDuns}" />
</transaction>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The output is not sorted by the $varWhse:
<?xml version="1.0" encoding="UTF-8"?>
<Partner partnerId="TradingPartner1">
<transaction varwhse="214">
<duns number="0044893600214"/>
</transaction>
<transaction varwhse="105">
<duns number="0044893600105"/>
</transaction>
<transaction varwhse="101">
<duns number="0044893600101"/>
</transaction>
<transaction varwhse="601">
<duns number="8949713340601"/>
</transaction>
<transaction varwhse="103">
<duns number="0044893600103"/>
</transaction>
</Partner>
I am wanting the data to come out like this:
<?xml version="1.0" encoding="UTF-8"?>
<Partner partnerId="TradingPartner1">
<transaction varwhse="101">
<duns number="0044893600101"/>
</transaction>
<transaction varwhse="103">
<duns number="0044893600103"/>
</transaction>
<transaction varwhse="105">
<duns number="0044893600105"/>
</transaction>
<transaction varwhse="214">
<duns number="0044893600214"/>
</transaction>
<transaction varwhse="601">
<duns number="8949713340601"/>
</transaction>
</Partner>
Anyone see what I am doing wrong or have another way? This is my first post on this site. It's a lot of information and I hope it makes sense.
One of the problems with your code is that the xsl:sort in your Header template is not the first instruction; xsl:sort has to be the first instruction. Another problem is that the sorting appears in the Header template, but it would have to be applied on an upper level. To correct these mistakes, some restructuring of your templates has to be performed.
For the following solution I chose to put all of the logic into one template - with two steps:
Fill a variable named mapping with a sub-data-structure with item elements.
Iterate over that variable with a sorted xsl:for-each and output the transaction elements.
The result looks like this:
<?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" encoding="UTF-8" indent="yes"/>
<xsl:include href="Duns_config.xslt"/>
<xsl:template match="Partner">
<Partner partnerId="{./#partnerId}">
<xsl:variable name="mapping">
<xsl:for-each select="Header">
<xsl:variable name="headerDuns">
<xsl:call-template name = "SHIPTODUNS">
<xsl:with-param name="Whse" select="#whse" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="varWhse">
<xsl:value-of select="substring($headerDuns, 11, 3)" />
</xsl:variable>
<item>
<whse><xsl:value-of select="$varWhse" /></whse>
<duns><xsl:value-of select="$headerDuns" /></duns>
</item>
</xsl:for-each>
</xsl:variable>
<xsl:for-each select="$mapping/item">
<xsl:sort select="whse" /> <!-- See how xsl:sort is the first instruction -->
<transaction varwhse="{whse}">
<duns number="{duns}"/>
</transaction>
</xsl:for-each>
</Partner>
</xsl:template>
</xsl:stylesheet>
And its output is:
<?xml version="1.0" encoding="UTF-8"?>
<Partner partnerId="TradingPartner1">
<transaction varwhse="101">
<duns number="0044893600101"/>
</transaction>
<transaction varwhse="103">
<duns number="0044893600103"/>
</transaction>
<transaction varwhse="105">
<duns number="0044893600105"/>
</transaction>
<transaction varwhse="214">
<duns number="0044893600214"/>
</transaction>
<transaction varwhse="601">
<duns number="8949713340601"/>
</transaction>
</Partner>
Which is as desired.
Apply sorting after getting the result of choose. What happen is you are still generating the xml output, but you are sorting it on the Header item level.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:include href="Duns_config.xslt"/>
<xsl:template match="Partner">
<xsl:variable name="partial">
<PartnerTemp partnerId="{./#partnerId}">
<xsl:apply-templates select="./Header" />
</PartnerTemp>
</xsl:variable>
<xsl:apply-templates select="$partial"></xsl:apply-templates>
</xsl:template>
<xsl:template match="PartnerTemp">
<Partner partnerId="{./#partnerId}">
<xsl:perform-sort select="transaction">
<xsl:sort select="#varwhse"/>
</xsl:perform-sort>
</Partner>
</xsl:template>
<xsl:template match="Header">
<xsl:variable name="headerDuns">
<xsl:call-template name = "SHIPTODUNS">
<xsl:with-param name="Whse" select="./#whse" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="varWhse">
<xsl:value-of select="substring($headerDuns, 11, 3)" />
</xsl:variable>
<xsl:for-each select="current()">
<transaction varwhse="{$varWhse}">
<duns number="{$headerDuns}" />
</transaction>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Combine two queries using WSO2 ESB

I've been trying to figure out how to get WSO2's ESB to make calls to two different APIs and combine their results into a single response, and running into nothing but trouble. At its most basic, I've got two backends I'm making requests to that respond something like this:
http://example.com/items:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<response xmlns="http://example.com/response">
<query name="items" xmlns="http://example.com/query">
<row>
<id>1</id>
<name>Item 1</name>
</row>
<row>
<id>2</id>
<name>Item 2</name>
</row>
</query>
</response>
</soapenv:Body>
</soapenv:Envelope>
http://example.com/parts:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<response xmlns="http://example.com/response">
<query name="parts" xmlns="http://example.com/query">
<row>
<id>1</id>
<part>Part 1.1</part>
</row>
<row>
<id>1</id>
<part>Part 1.2</part>
</row>
<row>
<id>1</id>
<part>Part 1.3</part>
</row>
<row>
<id>2</id>
<part>Part 2.1</part>
</row>
<row>
<id>2</id>
<part>Part 2.2</part>
</row>
</query>
</response>
</soapenv:Body>
</soapenv:Envelope>
I'd like to request both of those, then combine their results to look something like this:
<items>
<item>
<id>1</id>
<name>Item 1</name>
<parts>
<part>
<id>1</id>
<name>Part 1.1</name>
</part>
<part>
<id>1</id>
<name>Part 1.2</name>
</part>
<part>
<id>1</id>
<name>Part 1.3</name>
</part>
</parts>
</item>
<item>
<id>2</id>
<name>Item 2</name>
<parts>
<part>
<id>2</id>
<name>Part 2.1</name>
</part>
<part>
<id>2</id>
<name>Part 2.2</name>
</part>
</parts>
</item>
</items>
Basically, every response from both APIs has a list of rows, each of which contains an id element. The ids in the call to /items are unique within that response, and each row in the response from parts has an id that ties it to a row from /items.
I've got the following API definition in the ESB:
<?xml version="1.0" encoding="UTF-8"?>
<api context="/item_list" name="ItemList" xmlns="http://ws.apache.org/ns/synapse">
<resource methods="POST" uri-template="/">
<inSequence>
<header name="Content-Type" scope="transport" value="text/xml; charset=utf-8"/>
<clone>
<target>
<sequence>
<send>
<endpoint>
<address format="soap11" uri="http://example.com/items"/>
</endpoint>
</send>
</sequence>
</target>
<target>
<sequence>
<send>
<endpoint>
<address format="soap11" uri="http://example.com/parts"/>
</endpoint>
</send>
</sequence>
</target>
</clone>
</inSequence>
<outSequence>
<aggregate>
<correlateOn expression="//*[name()='response']/*[name()='query']/*[name()='row']/*[name()='id']" />
<completeCondition>
<messageCount max="2" min="2"/>
</completeCondition>
<onComplete expression="//*[name()='response']/*[name()='query']/*[name()='row']">
<send/>
</onComplete>
</aggregate>
</outSequence>
<faultSequence/>
</resource>
</api>
The inSequence here is heavily simplified, but it does send two valid queries and gets back the expected responses. The outSequence as it's written here never sends a response to the client or logs an error on the server. If I remove the correlateOn element from aggregate, I get back a single row, seemingly at random, from one of the two API calls. I think correlateOn is something I want to be using here, but I can't find any useful documentation on it from either WSO2 or Apache, so I'm sure I'm using it incorrectly. My XPath background is pretty weak, so I'm sure that expression could also use some work.
Am I at least on the right track here with the clone/aggregate pattern? How would I go about combining the results from these two queries into something similar to my example? If I can get something even sort of close, I should be able to do the rest with XSLT.
take a look at this demo:
Backend 1 with the items response:
<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse"
name="items"
transports="https http"
startOnLoad="true">
<target>
<inSequence>
<payloadFactory media-type="xml">
<format>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<response xmlns="http://example.com/response">
<query xmlns="http://example.com/query" name="items">
<row>
<id>1</id>
<name>Item 1</name>
</row>
<row>
<id>2</id>
<name>Item 2</name>
</row>
</query>
</response>
</soapenv:Body>
</soapenv:Envelope>
</format>
<args/>
</payloadFactory>
<log level="full"/>
<loopback/>
</inSequence>
<outSequence>
<send/>
</outSequence>
<faultSequence/>
</target>
</proxy>
Backend 2 with the parts response:
<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse"
name="parts"
transports="https http"
startOnLoad="true">
<target>
<inSequence>
<payloadFactory media-type="xml">
<format>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<response xmlns="http://example.com/response">
<query xmlns="http://example.com/query" name="parts">
<row>
<id>1</id>
<part>Part 1.1</part>
</row>
<row>
<id>1</id>
<part>Part 1.2</part>
</row>
<row>
<id>1</id>
<part>Part 1.3</part>
</row>
<row>
<id>2</id>
<part>Part 2.1</part>
</row>
<row>
<id>2</id>
<part>Part 2.2</part>
</row>
</query>
</response>
</soapenv:Body>
</soapenv:Envelope>
</format>
<args/>
</payloadFactory>
<log level="full"/>
<loopback/>
</inSequence>
<outSequence>
<send/>
</outSequence>
<faultSequence/>
</target>
</proxy>
My API calling backend 1 and backend 2 and transforming with xslt:
<?xml version="1.0" encoding="UTF-8"?>
<api xmlns="http://ws.apache.org/ns/synapse"
name="ItemList"
context="/item_list">
<resource methods="POST" uri-template="/">
<inSequence>
<header name="Action" scope="default" value="urn:mediate"/>
<call>
<endpoint>
<address uri="http://localhost:8283/services/items.itemsHttpSoap11Endpoint"
format="soap11"/>
</endpoint>
</call>
<enrich>
<source type="inline" clone="true">
<Payloads/>
</source>
<target type="property" property="Items"/>
</enrich>
<enrich>
<source clone="true" xpath="$body/*"/>
<target action="child" xpath="$ctx:Items"/>
</enrich>
<payloadFactory media-type="xml">
<format>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body/>
</soapenv:Envelope>
</format>
<args/>
</payloadFactory>
<call>
<endpoint>
<address uri="http://localhost:8283/services/parts.partsHttpSoap11Endpoint"
format="soap11"/>
</endpoint>
</call>
<enrich>
<source clone="true" xpath="$body/*[name()='response']/*[name()='query']"/>
<target type="property" property="Parts"/>
</enrich>
<enrich>
<source type="property" clone="true" property="Parts"/>
<target action="child" xpath="$ctx:Items"/>
</enrich>
<enrich>
<source type="property" property="Items"/>
<target type="body"/>
</enrich>
<xslt key="transformTwoSourcesToOneResult"/>
<loopback/>
</inSequence>
<outSequence>
<send/>
</outSequence>
<faultSequence/>
</resource>
</api>
And my xslt transformation:
<?xml version="1.0" encoding="UTF-8"?>
<localEntry key="transformTwoSourcesToOneResult" xmlns="http://ws.apache.org/ns/synapse">
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://example.com/query"
xmlns:ns1="http://example.com/response"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:exslt="http://exslt.org/common"
xmlns:saxon="http://saxon.sf.net/"
xmlns:syn="http://ws.apache.org/ns/synapse"
exclude-result-prefixes="ns0 ns1 xs">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="var1_instance_Payloads" select="."/>
<items>
<xsl:for-each select="$var1_instance_Payloads/syn:Payloads">
<xsl:variable name="var2_Payloads" select="."/>
<xsl:for-each select="$var2_Payloads/ns1:response/ns0:query/ns0:row">
<xsl:variable name="var2_row" select="."/>
<item>
<id>
<xsl:value-of select="number(string($var2_row/ns0:id))"/>
</id>
<name>
<xsl:value-of select="string($var2_row/ns0:name)"/>
</name>
<parts>
<xsl:for-each select="$var2_Payloads/ns0:query/ns0:row">
<xsl:variable name="var4_row" select="."/>
<xsl:if test="string((number(string($var2_row/ns0:id)) = number(string($var4_row/ns0:id)))) != 'false'">
<part>
<id>
<xsl:value-of select="number(string($var4_row/ns0:id))"/>
</id>
<name>
<xsl:value-of select="string($var4_row/ns0:part)"/>
</name>
</part>
</xsl:if>
</xsl:for-each>
</parts>
</item>
</xsl:for-each>
</xsl:for-each>
</items>
</xsl:template>
</xsl:stylesheet>
</localEntry>
The API response:
<items xmlns="http://ws.apache.org/ns/synapse" xmlns:syn="http://ws.apache.org/ns/synapse" xmlns:saxon="http://saxon.sf.net/" xmlns:exslt="http://exslt.org/common">
<item>
<id>1</id>
<name>Item 1</name>
<parts>
<part>
<id>1</id>
<name>Part 1.1</name>
</part>
<part>
<id>1</id>
<name>Part 1.2</name>
</part>
<part>
<id>1</id>
<name>Part 1.3</name>
</part>
</parts>
</item>
<item>
<id>2</id>
<name>Item 2</name>
<parts>
<part>
<id>2</id>
<name>Part 2.1</name>
</part>
<part>
<id>2</id>
<name>Part 2.2</name>
</part>
</parts>
</item>
</items>

WSO2 XSLT Mediator

Below is the sample from my sequence file
<log level="full">
<property name="upsertResponse" expression="$body" scope="default"/>
</log>
<xslt description="QuerySfdcForNONcsfTransactions" key="conf:xslt/QuerySfdcForNONcsfTransactions.xslt"/>
<log level="full">
<property name="MESSAGE" value="Before SFDC Connection 9010"/>
</log>
<salesforce.retrieve configKey="sfdc_connection">
<fieldList>id,Opportunity_External_ID__c,Contact_External_ID__c</fieldList>
<objectType>Opportunity</objectType>
<objectIDS xmlns:sfdc="sfdc">{//sfdc:sObjects}</objectIDS>
</salesforce.retrieve>
<log level="full">
<property name="MESSAGE" value="After SFDC connection here 9010"/>
</log>
<log level="full">
<property name="SFDCresponse" expression="$body" scope="default"/>
</log>
below is my xslt code
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns="http://ws.rb.com/datamodel" xmlns:sfdc="sfdc">
<xsl:template match="/">
<sfdc:sObjects xmlns:sfdc="sfdc" xmlns:ns="http://ws.rb.com/datamodel" type="Opportunity">
<xsl:for-each select="//upsertResponse/result">
<xsl:if test="'true' = success">
<sfdc:Ids>
<xsl:value-of select="id">
</xsl:value-of>
</sfdc:Ids>
</xsl:if>
</xsl:for-each>
</sfdc:sObjects>
</xsl:template>
</xsl:stylesheet>
Below is the output needed
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
<soapenv:Body>
<sfdc:sObjects xmlns:sfdc="sfdc" xmlns:ns="http://ws.rb.com/datamodel">
<sfdc:Ids>a071a000009Ke8gAAC</sfdc:Ids>
<sfdc:Ids>006R000000954zQIAQ</sfdc:Ids>
</sfdc:sObjects>
</soapenv:Body>
</soapenv:Envelope>
But below is the output which i got after transformation which differs from my desired output.
<?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="urn:partner.soap.sforce.com"><soapenv:Header><LimitInfoHeader><limitInfo><current>21251</current><limit>5000000</limit><type>API REQUESTS</type></limitInfo></LimitInfoHeader></soapenv:Header><soapenv:Body><sfdc:sObjects xmlns:sfdc="sfdc" xmlns:ns="http://ws.rb.com/datamodel" type="Opportunity"></sfdc:sObjects></soapenv:Body></soapenv:Envelope>
After executing the sequence below is the log file
WSAction: , SOAPAction: , MessageID: urn:uuid:9d93d66d-8a60-4ecb-9a97-3fef5cb99b23, Direction: request, upsertResponse = <soapenv:Body xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><upsertResponse xmlns="urn:partner.soap.sforce.com"><result><created>false</created><id>0061a00000BoFD0AAN</id><success>true</success></result></upsertResponse></soapenv:Body>, Envelope: <?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="urn:partner.soap.sforce.com"><soapenv:Header><LimitInfoHeader><limitInfo><current>21251</current><limit>5000000</limit><type>API REQUESTS</type></limitInfo></LimitInfoHeader></soapenv:Header><soapenv:Body><upsertResponse><result><created>false</created><id>0061a00000BoFD0AAN</id><success>true</success></result></upsertResponse></soapenv:Body></soapenv:Envelope>
WSAction: , SOAPAction: , MessageID: urn:uuid:9d93d66d-8a60-4ecb-9a97-3fef5cb99b23, Direction: request, MESSAGE = Before SFDC Connection 9010, Envelope: <?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="urn:partner.soap.sforce.com"><soapenv:Header><LimitInfoHeader><limitInfo><current>21251</current><limit>5000000</limit><type>API REQUESTS</type></limitInfo></LimitInfoHeader></soapenv:Header><soapenv:Body><sfdc:sObjects xmlns:sfdc="sfdc" xmlns:ns="http://ws.rb.com/datamodel" type="Opportunity"></sfdc:sObjects></soapenv:Body></soapenv:Envelope>
Please assist me where i am wrong
Thanks

Resources