BizTalk XSLT Mapping For Grouping and Sorting with Multiple Records - xslt

Input Message :
The Input File Has Three Records inside the Detail is in the order “Member” , “Product” and “Dependent” , In each Record there is a common Field which is “ Identifier” For Some reason we getting like Each Member and Product are looped into one detail and Each dependent is looping into separate Detail
................................................................................
<ns0:Root xmlns:ns0="Test">
<Detail>
<Member>
<Name>Jerry</Name>
<Address>Miami</Address>
<PhoneNumber>7008084201</PhoneNumber>
<Identifier>225692067</Identifier>
</Member>
<Product>
<Name>Phone</Name>
<Type>Personal</Type>
<Serial>000000111111</Serial>
<Identifier>225692067</Identifier>
</Product>
</Detail>
<Detail>
<Dependent>
<DependentName>Tom</DependentName>
<DependentAddress>Miami</DependentAddress>
<DependentPhoneNumber>7228084302</DependentPhoneNumber>
<Identifier>225692067</Identifier>
</Dependent>
</Detail>
<Detail>
<Dependent>
<DependentName>Tom1</DependentName>
<DependentAddress>Miami</DependentAddress>
<DependentPhoneNumber>8228084302</DependentPhoneNumber>
<Identifier>225692067</Identifier>
</Dependent>
</Detail>
<Detail>
<Dependent>
<DependentName>Tom2</DependentName>
<DependentAddress>Miami</DependentAddress>
<DependentPhoneNumber>9228084302</DependentPhoneNumber>
<Identifier>225692067</Identifier>
</Dependent>
</Detail>
<Detail>
<Member>
<Name>John</Name>
<Address>Kansas</Address>
<PhoneNumber>5007684306</PhoneNumber>
<Identifier>699039521</Identifier>
</Member>
<Product>
<Name>Xbox</Name>
<Type>Personal</Type>
<Serial>000000222222</Serial>
<Identifier>699039521</Identifier>
</Product>
</Detail>
<Detail>
<Member>
<Name>Larry</Name>
<Address>Newjersey</Address>
<PhoneNumber>6004567307</PhoneNumber>
<Identifier>230903815</Identifier>
</Member>
<Product>
<Name>Iphone</Name>
<Type>Personal</Type>
<Serial>0000003333333</Serial>
<Identifier>230903815</Identifier>
</Product>
</Detail>
<Detail>
<Dependent>
<DependentName>Luis</DependentName>
<DependentAddress>Miami</DependentAddress>
<DependentPhoneNumber>7897684302</DependentPhoneNumber>
<Identifier>230903815</Identifier>
</Dependent>
</Detail>
<Detail>
<Dependent>
<DependentName>LuisMead</DependentName>
<DependentAddress>Miami</DependentAddress>
<DependentPhoneNumber>7229876302</DependentPhoneNumber>
<Identifier>230903815</Identifier>
</Dependent>
</Detail>
</ns0:Root>
Expected OutPut XML :
The OutPut File is also Similar to the Input File but the order is “Member” , “Dependent” and “Product” . The Common Field “Identifier” is the common on in this case also.The idea is to make Detail to loop on “Member” , “Dependent” and “Product” order.
......................................................................................
<ns0:Root xmlns:ns0="Test">
<Detail>
<Member>
<Name>Jerry</Name>
<Address>Miami</Address>
<PhoneNumber>7008084201</PhoneNumber>
<Identifier>225692067</Identifier>
</Member>
<Dependent>
<DependentName>Tom</DependentName>
<DependentAddress>Miami</DependentAddress>
<DependentPhoneNumber>7228084302</DependentPhoneNumber>
<Identifier>225692067</Identifier>
</Dependent>
<Dependent>
<DependentName>Tom1</DependentName>
<DependentAddress>Miami</DependentAddress>
<DependentPhoneNumber>8228084302</DependentPhoneNumber>
<Identifier>225692067</Identifier>
</Dependent>
<Dependent>
<DependentName>Tom2</DependentName>
<DependentAddress>Miami</DependentAddress>
<DependentPhoneNumber>9228084302</DependentPhoneNumber>
<Identifier>225692067</Identifier>
</Dependent>
<Product>
<Name>Phone</Name>
<Type>Personal</Type>
<Serial>000000111111</Serial>
<Identifier>225692067</Identifier>
</Product>
</Detail>
<Detail>
<Member>
<Name>John</Name>
<Address>Kansas</Address>
<PhoneNumber>5007684306</PhoneNumber>
<Identifier>699039521</Identifier>
</Member>
<Product>
<Name>Xbox</Name>
<Type>Personal</Type>
<Serial>000000222222</Serial>
<Identifier>699039521</Identifier>
</Product>
</Detail>
<Detail>
<Member>
<Name>Larry</Name>
<Address>Newjersey</Address>
<PhoneNumber>6004567307</PhoneNumber>
<Identifier>230903815</Identifier>
</Member>
<Dependent>
<DependentName>Luis</DependentName>
<DependentAddress>Miami</DependentAddress>
<DependentPhoneNumber>7897684302</DependentPhoneNumber>
<Identifier>230903815</Identifier>
</Dependent>
<Dependent>
<DependentName>LuisMead</DependentName>
<DependentAddress>Miami</DependentAddress>
<DependentPhoneNumber>7229876302</DependentPhoneNumber>
<Identifier>230903815</Identifier>
</Dependent>
<Product>
<Name>Iphone</Name>
<Type>Personal</Type>
<Serial>0000003333333</Serial>
<Identifier>230903815</Identifier>
</Product>
</Detail>
</ns0:Root>
Need Suggestion on writing XSLT 1.0 Code for this.

You can do this in XSLT-1.0 with Muenchian Grouping. Search for it on StackOverflow and you will find a lot of examples. Applying it created the following answer:
Create an xsl:key with the nodes Member|Dependent|Product using its Identifier element value as a key
Create a sortingOrder variable which provides indices for sorting the entries in the inner xsl:for-each
Match and copy the root node /ns0:Root with a template
Loop over all children of the Detail elements in a xsl:for-each. The expression applies the Muenchian Grouping method
Create a Detail elements and loop over the results of the previous xsl:for-each sorted by the index of the occurrence of the name of the current element in the sortingOrder variable. Copy its content. The method of ordering the elements was taken from this SO answer: "Sorting XML in XSLT based on a list of values".
The stylesheet could look like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns0="Test">
<xsl:output indent="yes"/>
<xsl:key name="id" match="Member|Dependent|Product" use="Identifier" />
<xsl:variable name="sortingOrder" select="'Member,Dependent,Product'" />
<xsl:template match="/ns0:Root">
<xsl:copy>
<xsl:for-each select="Detail/*[generate-id() = generate-id(key('id',Identifier)[1])]">
<Detail>
<xsl:for-each select="key('id',Identifier)">
<xsl:sort data-type="number" select="string-length(substring-before($sortingOrder,local-name()))" />
<xsl:copy-of select="."/>
</xsl:for-each>
</Detail>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The output should be as desired.

Related

XSLT3 Streaming for appending integer position of node

I have a large XML file to transform using XSLT to append the integer position of sibling node . I’m using XSLT3 streaming and accumulators. I did get desired output. However, my code looks so lengthy that I’m unable to simplify my code. I also need to group same sibling nodes as sibling nodes in the source xml is not grouped always. Could someone help me here please?
Requirement: Sibling nodes such as Positions, Payments etc.. need to be appended with their corresponding integer position such as <Locations1>, <Locations2>etc.<Payments1>,< Payments2> etc..
Now that I have declared two accumulators, each for each sibling nodes. However, my source XML has many sibling nodes.. I’m not sure if I need to use as many accumulators and template match as my sibling nodes.
Input XML
``
<?xml version="1.0" encoding="UTF-8"?>
<Members>
<Member>
<Name>
<fname>Fred</fname>
<id>1234</id>
</Name>
<Locations>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations>
<Locations>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations>
<Payments>
<amount>1000</amount>
<currency>USD</currency>
</Payments>
<Payments>
<amount>1000</amount>
<currency>USD</currency>
</Payments>
<Locations>
<name>New York</name>
<days>5</days>
<hours>40</hours>
</Locations>
<Locations>
<name>Boston</name>
<days>4</days>
<hours>32</hours>
</Locations>
</Member>
<Member>
<Name>
<fname>Jack</fname>
<id>4567</id>
</Name>
<Locations>
<name>New York</name>
<days>5</days>
<hours>30</hours>
</Locations>
<Locations>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations>
<Payments>
<amount>1500</amount>
<currency>USD</currency>
</Payments>
<Payments>
<amount>1800</amount>
<currency>USD</currency>
</Payments>
</Member>
</Members>
``
Expected Output
``
<?xml version="1.0" encoding="UTF-8"?>
<Members>
<Member>
<Name>
<fname>Fred</fname>
<id>1234</id>
</Name>
<Locations_1>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations_1>
<Locations_2>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations_2>
<Locations_3>
<name>New York</name>
<days>5</days>
<hours>40</hours>
</Locations_3>
<Locations_4>
<name>Boston</name>
<days>4</days>
<hours>32</hours>
</Locations_4>
<Payments_1>
<amount>1000</amount>
<currency>USD</currency>
</Payments_1>
<Payments_2>
<amount>1000</amount>
<currency>USD</currency>
</Payments_2>
</Member>
<Member>
<Name>
<fname>Jack</fname>
<id>4567</id>
</Name>
<Locations_1>
<name>New York</name>
<days>5</days>
<hours>30</hours>
</Locations_1>
<Locations_2>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations_2>
<Payments_1>
<amount>1500</amount>
<currency>USD</currency>
</Payments_1>
<Payments_2>
<amount>1800</amount>
<currency>USD</currency>
</Payments_2>
</Member>
</Members>
``
Current code
``
<?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="xs" version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:mode streamable="yes" on-no-match="shallow-copy" use-accumulators="#all"/>
<xsl:accumulator name="loc-count" as="xs:integer" initial-value="0" streamable="yes">
<xsl:accumulator-rule match="Member" select="0"/>
<xsl:accumulator-rule match="Member/Locations" select="$value + 1"/>
</xsl:accumulator>
<xsl:accumulator name="pay-count" as="xs:integer" initial-value="0" streamable="yes">
<xsl:accumulator-rule match="Member" select="0"/>
<xsl:accumulator-rule match="Member/Payments" select="$value + 1"/>
</xsl:accumulator>
<xsl:template match="Locations">
<xsl:element name="Locations_{accumulator-before('loc-count')}">
<xsl:copy-of select="#* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="Payments">
<xsl:element name="Payments_{accumulator-before('pay-count')}">
<xsl:copy-of select="#* | node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
``
Current Output
<?xml version="1.0" encoding="UTF-8"?>
<Members>
<Member>
<Name>
<fname>Fred</fname>
<id>1234</id>
</Name>
<Locations_1>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations_1>
<Locations_2>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations_2>
<Payments_1>
<amount>1000</amount>
<currency>USD</currency>
</Payments_1>
<Payments_2>
<amount>1000</amount>
<currency>USD</currency>
</Payments_2>
<Locations_3>
<name>New York</name>
<days>5</days>
<hours>40</hours>
</Locations_3>
<Locations_4>
<name>Boston</name>
<days>4</days>
<hours>32</hours>
</Locations_4>
</Member>
<Member>
<Name>
<fname>Jack</fname>
<id>4567</id>
</Name>
<Locations_1>
<name>New York</name>
<days>5</days>
<hours>30</hours>
</Locations_1>
<Locations_2>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations_2>
<Payments_1>
<amount>1500</amount>
<currency>USD</currency>
</Payments_1>
<Payments_2>
<amount>1800</amount>
<currency>USD</currency>
</Payments_2>
</Member>
</Members>
If you want to group the Member child elements by node-name() then I think you need to wrap the xsl:for-each-group into xsl:fork:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="#all" version="3.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:mode on-no-match="shallow-copy" streamable="yes" use-accumulators="counters"/>
<xsl:accumulator name="counters" as="map(xs:QName, xs:integer)" initial-value="map{}" streamable="yes">
<xsl:accumulator-rule match="Member" select="map{}"/>
<xsl:accumulator-rule match="Member/*"
select="map:put($value, node-name(), if (map:contains($value, node-name())) then map:get($value, node-name()) + 1 else 1)"/>
</xsl:accumulator>
<xsl:template match="Member">
<xsl:copy>
<xsl:fork>
<xsl:for-each-group select="*" group-by="node-name()">
<xsl:apply-templates select="current-group()"/>
</xsl:for-each-group>
</xsl:fork>
</xsl:copy>
</xsl:template>
<xsl:template match="Member/*">
<xsl:element name="{node-name()}_{accumulator-before('counters')(node-name())}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
This approach only shows the grouping, it doesn't try to special case Name elements or some other way to not output an index if there is only one such element.
Firstly, my sympathy. XML that uses names like Payments_1 and Payments_2 is really bad news, someone is going to hate you for generating it like this. But if that's the kind of XML you've been told to produce, I guess it's not your job to question it.
As far as the requirements are concerned, you haven't made it clear whether the various kinds of sibling nodes are always grouped as in your example (all Locations, then all Payments, etc), or whether they can be interleaved.
One way you might be able to reduce the volume of code is by having a single accumulator holding a map. The map would use element names as the key and the current sibling count for that element as the value.
<accumulator name="counters" as="map(xs:QName, xs:integer)" initial-value="map{}">
<xsl:accumulator-rule match="Member" select="map{}"/>
<xsl:accumulator-rule match="Member/*" select="map:put($value, node-name(.), if (map:contains($value, node-name(.)) then map:get($value, node-name(.))+1 else 1"/>
</accumulator>
<xsl:template match="Members/*">
<xsl:element name="{name()}_{accumulator-before('counters')(node-name(.))}">
....
Another way to do the conditional map:put is
map:put($value, node-name(.), ($value(node-name(.)), 0)[1] + 1)

XSLT Transformation : for each

Below is the xml input to xslt
<?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="urn:partner.soap.sforce.com" xmlns:sf="urn:sobject.partner.soap.sforce.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Header><LimitInfoHeader><limitInfo><current>4930</current><limit>5000000</limit><type>API REQUESTS</type></limitInfo></LimitInfoHeader></soapenv:Header>
<soapenv:Body>
<queryResponse>
<result xsi:type="QueryResult">
<done>true</done>
<queryLocator xsi:nil="true"/>
<records xsi:type="sf:sObject">
<sf:type>Account</sf:type>
<sf:Id xsi:nil="true"/>
<sf:Contacts xsi:type="QueryResult">
<done>true</done>
<queryLocator xsi:nil="true"/>
<records xsi:type="sf:sObject">
<sf:type>Contact</sf:type>
<sf:Id>0031700000ThqzkAAB</sf:Id>
<sf:Id>0031700000ThqzkAAB</sf:Id>
</records>
<size>1</size>
</sf:Contacts>
<sf:Opportunities xsi:type="QueryResult">
<done>true</done>
<queryLocator xsi:nil="true"/>
<records xsi:type="sf:sObject">
<sf:type>Opportunity</sf:type>
<sf:Id>0061700000BB1kRAAT</sf:Id>
<sf:Id>0061700000BB1kRAAT</sf:Id>
</records>
<size>1</size>
</sf:Opportunities>
</records>
***<records xsi:type="sf:sObject">
<sf:type>Account</sf:type>
<sf:Id xsi:nil="true"/>
<sf:Contacts xsi:type="QueryResult">
<done>true</done>
<queryLocator xsi:nil="true"/>
<records xsi:type="sf:sObject">
<sf:type>Contact</sf:type>
<sf:Id>0031a00000Kdu8zAAB</sf:Id>
<sf:Id>0031a00000Kdu8zAAB</sf:Id>
</records>
<size>1</size>
</sf:Contacts>
<sf:Opportunities xsi:type="QueryResult">
<done>true</done>
<queryLocator xsi:nil="true"/>
<records xsi:type="sf:sObject">
<sf:type>Opportunity</sf:type>
<sf:Id>0061a00000Ej6JwAAJ</sf:Id>
<sf:Id>0061a00000Ej6JwAAJ</sf:Id>
</records>
<size>1</size>
</sf:Opportunities>
</records>
<records xsi:type="sf:sObject">
<sf:type>Account</sf:type>
<sf:Id xsi:nil="true"/>
<sf:Contacts xsi:type="QueryResult">
<done>true</done>
<queryLocator xsi:nil="true"/>
<records xsi:type="sf:sObject">
<sf:type>Contact</sf:type>
<sf:Id>0031a00000HcNSDAA3</sf:Id>
<sf:Id>0031a00000HcNSDAA3</sf:Id>
</records>
<size>1</size>
</sf:Contacts>
<sf:Opportunities xsi:type="QueryResult">
<done>true</done>
<queryLocator xsi:nil="true"/>
<records xsi:type="sf:sObject">
<sf:type>Opportunity</sf:type>
<sf:Id>0061a00000Ej6JDAAZ</sf:Id>
<sf:Id>0061a00000Ej6JDAAZ</sf:Id>
</records>
<size>1</size>
</sf:Opportunities>
</records>***
<size>3</size>
</result>
</queryResponse>
</soapenv:Body></soapenv:Envelope>
below is the 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="urn:partner.soap.sforce.com"
xmlns:sf="urn:sobject.partner.soap.sforce.com"
xmlns:sfdc="sfdc">
<xsl:template match="/">
<sfdc:sObjects xmlns:sfdc="sfdc" type="OpportunityContactRole">
<xsl:for-each select="//ns:queryResponse/ns:result/ns:records">
<xsl:if test="//ns:records/sf:Contacts/ns:done/text() = 'true' and //ns:records/sf:Opportunities/ns:done/text() = 'true'">
<sfdc:sObject>
<sfdc:ContactId>
<xsl:value-of select="//ns:records/sf:Contacts/ns:records/sf:Id[1]/text()"/>
</sfdc:ContactId>
<sfdc:IsPrimary>True</sfdc:IsPrimary>
<OpportunityId>
<xsl:value-of select="//ns:records/sf:Opportunities/ns:records/sf:Id[1]/text()"/>
</OpportunityId>
<sfdc:Role>Purchaser</sfdc:Role>
</sfdc:sObject>
</xsl:if>
</xsl:for-each>
</sfdc:sObjects>
</xsl:template>
</xsl:stylesheet>
below is the output which i got
<?xml version="1.0" encoding="utf-8"?>
<sfdc:sObjects xmlns:sfdc="sfdc" xmlns:ns="urn:partner.soap.sforce.com"
xmlns:sf="urn:sobject.partner.soap.sforce.com" type="OpportunityContactRole">
<sfdc:sObject>
<sfdc:ContactId>0031700000ThqzkAAB</sfdc:ContactId>
<sfdc:IsPrimary>True</sfdc:IsPrimary>
<OpportunityId>0061700000BB1kRAAT</OpportunityId>
<sfdc:Role>Purchaser</sfdc:Role>
</sfdc:sObject>
<sfdc:sObject>
<sfdc:ContactId>0031700000ThqzkAAB</sfdc:ContactId>
<sfdc:IsPrimary>True</sfdc:IsPrimary>
<OpportunityId>0061700000BB1kRAAT</OpportunityId>
<sfdc:Role>Purchaser</sfdc:Role>
</sfdc:sObject>
<sfdc:sObject>
<sfdc:ContactId>0031700000ThqzkAAB</sfdc:ContactId>
<sfdc:IsPrimary>True</sfdc:IsPrimary>
<OpportunityId>0061700000BB1kRAAT</OpportunityId>
<sfdc:Role>Purchaser</sfdc:Role>
</sfdc:sObject>
</sfdc:sObjects>
here in the above generated output, the iteration is happening sucessfully, but only the first records values are considered
i.e 0031700000ThqzkAAB and 0061700000BB1kRAAT
why the second and third record values (highlighted in the input xml) does exists in the output.? not able to understand.
in summary below is the desired output
<?xml version="1.0" encoding="utf-8"?>
<sfdc:sObjects xmlns:sfdc="sfdc" xmlns:ns="urn:partner.soap.sforce.com"
xmlns:sf="urn:sobject.partner.soap.sforce.com" type="OpportunityContactRole">
<sfdc:sObject>
<sfdc:ContactId>0031700000ThqzkAAB</sfdc:ContactId>
<sfdc:IsPrimary>True</sfdc:IsPrimary>
<OpportunityId>0061700000BB1kRAAT</OpportunityId>
<sfdc:Role>Purchaser</sfdc:Role>
</sfdc:sObject>
<sfdc:sObject>
<sfdc:ContactId>0031a00000Kdu8zAAB</sfdc:ContactId>
<sfdc:IsPrimary>True</sfdc:IsPrimary>
<OpportunityId>0061a00000Ej6JwAAJ</OpportunityId>
<sfdc:Role>Purchaser</sfdc:Role>
</sfdc:sObject>
<sfdc:sObject>
<sfdc:ContactId>0031a00000HcNSDAA3</sfdc:ContactId>
<sfdc:IsPrimary>True</sfdc:IsPrimary>
<OpportunityId>0061a00000Ej6JDAAZ</OpportunityId>
<sfdc:Role>Purchaser</sfdc:Role>
</sfdc:sObject>
</sfdc:sObjects>
requesting to please help me output
When you are inside of your xsl:for-each, the context is //ns:queryResponse/ns:result/ns:records.
When you do your xsl:value-of, you are starting your select with // which is starting from the beginning of the document. Try making the select relative to the current context...
<xsl:template match="/">
<sfdc:sObjects xmlns:sfdc="sfdc" type="OpportunityContactRole">
<xsl:for-each select="//ns:queryResponse/ns:result/ns:records">
<xsl:if test="sf:Contacts/ns:done = 'true' and sf:Opportunities/ns:done = 'true'">
<sfdc:sObject>
<sfdc:ContactId>
<xsl:value-of select="sf:Contacts/ns:records/sf:Id[1]"/>
</sfdc:ContactId>
<sfdc:IsPrimary>True</sfdc:IsPrimary>
<OpportunityId>
<xsl:value-of select="sf:Opportunities/ns:records/sf:Id[1]"/>
</OpportunityId>
<sfdc:Role>Purchaser</sfdc:Role>
</sfdc:sObject>
</xsl:if>
</xsl:for-each>
</sfdc:sObjects>
</xsl:template>
Edit: You should also make the xpaths in the xsl:if relative to the current context. Updated the template above.
You could also move the test to a predicate in the xsl:for-each and remove the xsl:if entirely...
<xsl:for-each select="//ns:queryResponse/ns:result/ns:records[sf:Contacts/ns:done = 'true' and sf:Opportunities/ns:done = 'true']">
<sfdc:sObject>
...
</sfdc:sObject>
</xsl:for-each>
Modify your xpath : //ns:records/sf:Contacts/ns:records/sf:Id[1]/text() to remove //ns:records
So it will be just sf:Contacts/ns:records/sf:Id[1]/text()
and
//ns:records/sf:Opportunities/ns:records/sf:Id[1]/text() to sf:Opportunities/ns:records/sf:Id[1]/text()

Transforming attribute values into xml element

Using xslt 1.0, I need to transform below input xml to output xml
--input xml
<Row>
<Column name="NUMBER" sqltype="int">123</Column>
<Column name="DEPT1" sqltype="int">A</Column>
<Column name="CUST_EMPTYPE" sqltype="int">1</Column>
<Column name="CUST_TIJD" sqltype="int">31</Column>
</Row>
--output xml
<EMPLOYEE xmlns="http://xmlns.oracle.com/Employee">
<NUMBER>123</NUMBER>
<DEPT1>IHC</DEPT1>
<CUST_EMPTYPE>1</LASTNAME>
<CUST_TIJD>31</FIRSTNAME>
</EMPLOYEE>
the Column names from input xml are not known at design time, the Column can grow..
Can anyone let me know how to achieve this?
Thank you very much,
Yes. It is quite simple. One minor difference is the deviation between your DEPT1 source and destination value (I really don't know where 'IHC' may have been coming from). I harmonized it in the following XSLT code which just sets the Column/#name nodes to new EMPLOYEE elements with the content of the old text() content:
<?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" />
<xsl:template match="/Row">
<EMPLOYEE xmlns="http:xmlns.oracle.com/Employee">
<xsl:for-each select="Column">
<xsl:element name="{#name}">
<xsl:value-of select="text()" />
</xsl:element>
</xsl:for-each>
</EMPLOYEE>
</xsl:template>
</xsl:stylesheet>
The result of this is:
<EMPLOYEE xmlns="http:xmlns.oracle.com/Employee">
<NUMBER>123</NUMBER>
<DEPT1>A</DEPT1>
<CUST_EMPTYPE>1</CUST_EMPTYPE>
<CUST_TIJD>31</CUST_TIJD>
</EMPLOYEE>

Unexpected result from xsltproc - libXML

I was helping another user when we ran into this problem.
I have this piece of XSLT where I try to create two keys, one with the first occurrence (in the same parent) of an element containing a given value on a descendant and a second one with all other occurrences for the same value on the descendant. (sorry about bad english)
This is the first key, for which the goal is to create a set with "The first of the siblings", for a given Record/ID, indexed by its generate-id() value:
<xsl:key name ="key1" match="DataPage[not(
preceding-sibling::DataPage/Record/ID = Record/ID
)]"
use="generate-id()"/>
In the second key I try to get all DapaPage elements which are "NOT the first of the siblings", for a given Record/ID, indexed by generate-id() of "The first of the siblings" with the same Record/ID:
<xsl:key name="key2" match="DataPage[
preceding-sibling::DataPage/Record/ID = Record/ID
]"
use="generate-id(preceding-sibling::DataPage[
Record/ID = current()/Record/ID
][last()])" />
And the templates
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="DataPage"/>
</xsl:copy>
</xsl:template>
<xsl:template match="DataPage">
<xsl:copy>
<xsl:for-each select="key('key1',generate-id())">
<Key1>
<xsl:copy-of select="."/>
</Key1>
</xsl:for-each>
<xsl:for-each select="key('key2',generate-id())">
<Key2>
<xsl:copy-of select="."/>
</Key2>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I tested the XSLT with this XML:
<?xml version="1.0" encoding="UTF-8"?>
<Page>
<DataPage>
<Record>
<ID>0</ID>
<DESC>AAA</DESC>
<AMOUNT>11</AMOUNT>
</Record>
</DataPage>
<DataPage>
<Record>
<ID>0</ID>
<DESC>BBB</DESC>
<AMOUNT>22</AMOUNT>
</Record>
</DataPage>
<DataPage>
<Record>
<ID>0</ID>
<DESC>CCC</DESC>
<AMOUNT>333</AMOUNT>
</Record>
</DataPage>
</Page>
Transforming it with Xalan I get, as expected, the following result:
<Page>
<DataPage>
<Key1>
<DataPage>
<Record>
<ID>0</ID>
<DESC>AAA</DESC>
<AMOUNT>11</AMOUNT>
</Record>
</DataPage>
</Key1>
<Key2>
<DataPage>
<Record>
<ID>0</ID>
<DESC>BBB</DESC>
<AMOUNT>22</AMOUNT>
</Record>
</DataPage>
</Key2>
<Key2>
<DataPage>
<Record>
<ID>0</ID>
<DESC>CCC</DESC>
<AMOUNT>333</AMOUNT>
</Record>
</DataPage>
</Key2>
</DataPage>
<DataPage/>
<DataPage/>
</Page>
However when I use the libXML xsltproc I get only this:
<Page>
<DataPage>
<Key1>
<DataPage>
<Record>
<ID>0</ID>
<DESC>AAA</DESC>
<AMOUNT>11</AMOUNT>
</Record>
</DataPage>
</Key1>
</DataPage>
<DataPage/>
<DataPage/>
</Page>
What is wrong with the construction of Key2, or is this a xslproc bug?
Hard to say for certain, but yes, this looks like an error in xsltproc. At least, I don't see anything wrong in your key constructor and Saxon 9.4.0.3 agrees with Xalan and not with xsltproc on the output.

How to exclude child elements in xml?

I have the following xml :
<?xml version="1.0" encoding="UTF-8"?>
<persons>
<person>
<phone type='home'>203-555-1212</phone>
<phone type='fax'>203-555-1212</phone>
<address type='home'>
<street>12 Main Street</street>
<city>Southbury</city>
<state>CT</state>
<zip>06488</zip>
</address>
<firstName>Charles</firstName>
<lastName>Smithington</lastName>
</person>
<person1>
<phone type='home'>58-99-44-999</phone>
<phone type='fax'>5788-9987-3365</phone>
<address type='home'>
<street>12 Main Street</street>
<city>Park Avenue</city>
<state>NY</state>
<zip>10025</zip>
</address>
<firstName>Mike</firstName>
<lastName>Shinoda</lastName>
</person1>
</persons>
Here i have to exclude the address field of the person whose state is CT whereas to include the address of the person whose state is NY.
<xsl:template match="persons">
<persons>
<xsl:apply-templates select="person"/>
<xsl:apply-templates select="person1"/>
</persons>
</xsl:template>
<xsl:template match="person">
<person>
<xsl:copy-of select="*[name() != 'address']"/>
<xsl:apply-templates select="address"/>
</person>
</xsl:template><xsl:template match="person1">
<person1>
<xsl:copy-of select="*[name() != 'address']"/>
<xsl:apply-templates select="address"/>
</person1>
</xsl:template>
<xsl:template match="address">
<xsl:if test="not(state='CT')">
<xsl:copy-of select="../address"/>
</xsl:if>
</xsl:template>
As you apply above transformation on provided xml then it will produce following result xml.
<person>
<phone type="home">203-555-1212</phone>
<phone type="fax">203-555-1212</phone>
<firstName>Charles</firstName>
<lastName>Smithington</lastName>
</person>
<person1>
<phone type="home">58-99-44-999</phone>
<phone type="fax">5788-9987-3365</phone>
<firstName>Mike</firstName>
<lastName>Shinoda</lastName>
<address type="home">
<street>12 Main Street</street>
<city>Park Avenue</city>
<state>NY</state>
<zip>10025</zip>
</address>
</person1>