0

I have been trying to write the XSLT to merge two different XMLs and produce a different XML output using the XSLT code using XSLT v2.0.

Below are my requirements.

Input XML1 is like this:

<?xml version="1.0" encoding="UTF-8"?>
<externalIdFields xmlns="urn:partner.soap.sforce.com/">
   <object name="Account">
      <ExternalID>
         <name>ExtAccountID__c</name>
         <type>string</type>
      </ExternalID>
   </object>
   <object name="User"/>
   <object name="Contact">
      <ExternalID>
         <name>ExtContactID__c</name>
         <type>string</type>
     </ExternalID>
   </object>
  <object name="Opportunity">
      <ExternalID>
        <name>ExtOpportunityID__c</name>
        <type>decimal</type>
     </ExternalID>
     <ExternalID>
        <name>UniqueAnotherOppID__c</name>
        <type>boolean</type>
     </ExternalID>
  </object>

and XML2 is like this:

<?xml version="1.0" encoding="UTF-8"?>
 <relationshipNames>
   <object name="Account">
      <reletionshipName>Parent</reletionshipName>
   </object>
   <object name="User">
      <reletionshipName>Owner</reletionshipName>
   </object>
   <object name="Contact">
     <reletionshipName>LookUpContact__r</reletionshipName>
   </object>
   <object name="Opportunity">
      <reletionshipName>LookUpOpportunity__r</reletionshipName>
   </object>
   <object name="Opportunity">
      <reletionshipName>AnotherRelationship__r</reletionshipName>
   </object>
</relationshipNames>

The first XML contains all the external ids for each and second XML contains all the relationship names for each object.

The output XML should be

<?xml version="1.0" encoding="UTF-8"?>
     <result xmlns:con = "http://www.approuter.com/schemas/cdk/config/">
       <con:field name = "Parent.ExtAccountID__c" label = "Parent.ExtAccountID__c">
         <con:type>string</con:type>
      </con:field>
      <con:field name = "LookUpContact__r.ExtContactID__c" label="LookUpContact__r.ExtContactID__c">
         <con:type>string</con:type>
      </con:field>
      <con:field name = "LookUpOpportunity__r.ExtOpportunityID__c" label = "LookUpOpportunity__r.ExtOpportunityID__c">
           <con:type>decimal</con:type>
      </con:field>
      <con:field name = "LookUpOpportunity__r.UniqueAnotherOppID__c." label = "LookUpOpportunity__r.UniqueAnotherOppID__c">
           <con:type>boolean</con:type>
      </con:field>
      <con:field name = "AnotherRelationship__r.ExtOpportunityID__c." label = "AnotherRelationship__r.ExtOpportunityID__c">
           <con:type>boolean</con:type>
       </con:field>
       <con:field name = "AnotherRelationship__r.UniqueAnotherOppID__c." label = "AnotherRelationship__r.UniqueAnotherOppID__c">
           <con:type>boolean</con:type>
       </con:field>
  </result>

The merge is simple. For each externalId for each object, create a con:field tag and here the value for name and label should be corresponding relationshipName for that object and externalId field name. It will be relationshiName.externalIdFieldName. The relationshipName for an object should be associated to externalIds of only that object.

How can we achieve this? I saw an XSLT to merge two XMLs. But I did not understand the "@* | node()" part of different examples. Here I did not find examples which parses two completely different XMLs.

Appreciate your help.

1 Answer 1

1

Here is my suggestion:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:sf="urn:partner.soap.sforce.com/"
  xmlns:con="http://www.approuter.com/schemas/cdk/config/"
  exclude-result-prefixes="sf">

<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:param name="names-url" select="'test2013080105.xml'"/>
<xsl:variable name="names-doc" select="doc($names-url)"/>

<xsl:key name="name" match="relationshipNames/object" use="@name"/>

<xsl:template match="sf:externalIdFields">
  <result>
    <xsl:apply-templates/>
  </result>
</xsl:template>

<xsl:template match="sf:object/sf:ExternalID">
  <xsl:apply-templates select="key('name', ../@name, $names-doc)">
    <xsl:with-param name="eid" select="current()"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="relationshipNames/object">
  <xsl:param name="eid"/>
  <con:field name="{reletionshipName}.{$eid/sf:name}" label="{reletionshipName}.{$eid/sf:name}">
    <con:type><xsl:value-of select="$eid/sf:type"/></con:type>
  </con:field>
</xsl:template>

</xsl:stylesheet>

The stylesheet takes a parameter names-url for the second file name/URL. Also make sure all element names and namespace URLs are as posted (i.e. reletionshipName) or make sure you edit the stylesheet to have the correct spelling like relationshipName if the real input has the correctly spelled element names.

Sign up to request clarification or add additional context in comments.

4 Comments

Hi Martin. Thank you so much for your help. It worked perfectly. Great help. Excellent XSLT. But the way, while merging, is it also possible to just append one XML to another ?
Usually you want to create well-formed document and if you simply append you would get two root elements which is then not well-formed. But you can of course write <xsl:template match="/*"><xsl:copy><xsl:copy-of select="node(), doc('file2.xml')/*/node()"/></xsl:copy></xsl:template> to preserve the root element of the main input and to copy all child nodes of the root element of the second input.
Hi Martin, I noticed that the XSLT fails to give proper output if xml2 contains one more <object> tag with name Opportunity for different relationshipName. In that case, the output should contain 2 more <con:field> tags for the new relationshipName for 2 Opportunities externalIds. Any suggestions to modify the above XSLT code ?
@RaghavendraNilekani, I have changed the code to make sure each referenced object creates some output. I think now the sample produces the number and kind of output you want for the edited sample input documents although the order of output elements is different from what you posted. Check whether the result is fine for you, if not, explain what defines the output order.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.