0

I have incoming XML message and each message have different schema. I want to transform that request into another schema using C#.Net as my XSLT processor. Here is simplified scenario of the situation I have.

Incoming request:

<?xml version="1.0" encoding="utf-8"?>
<Request xmlns="http://www.example.com/api">
  <SourceId>SourceId1</SourceId>
  <RequestId>RequestId1</RequestId>
  <StatusEvent>
    <TenderId>TenderId1</TenderId>
    <EventCode>TENDER_STARTED</EventCode>
  </StatusEvent>
</Request>

Translate to:

<?xml version="1.0" encoding="utf-8"?>
<TransactionStatus xmlns="http://www.example1.com/api">
  <RequestId>RequestId1</RequestId>
  <TransactionId>TenderId1</TransactionId>
  <Event>TRANSACTION_STARTED</Event>
</TransactionStatus>

Incoming request:

<?xml version="1.0" encoding="utf-8"?>
<Response xmlns="http://www.example.com/api">
  <SourceId>SourceId1</SourceId>
  <RequestId>RequestId1</RequestId>
   <TenderCreated>
    <TenderId>TenderId1</TenderId>
  </TenderCreated>
 </Response>

Translate to:

 <?xml version="1.0" encoding="utf-8"?>
<TransactionStarted xmlns="http://www.example1.com/api">
  <RequestId>RequestId1</RequestId>
  <TransactionId>TenderId1</TransactionId>
 </TransactionStarted>

Here is the XSLT I'm currently using to achieve above result,

     <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:ns0="http://www.example.com/api"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="ns0 xs">
  <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
  <xsl:template match="text()"/>
  <xsl:template match="ns0:StatusEvent[1]">
    <TransactionStatus
        xmlns="http://www.example1.com/api">
      <RequestId>
        <xsl:value-of select="//ns0:RequestId"/>
      </RequestId>
      <TransactionId>
        <xsl:value-of select="ns0:TenderId"/>
      </TransactionId>
      <Event>
        <xsl:value-of select="ns0:EventCode"/>
      </Event>
    </TransactionStatus>
  </xsl:template>

  <xsl:template match="ns0:TenderCreated[1]">
    <TransactionStarted
        xmlns="http://www.example1.com/api">
      <RequestId>
        <xsl:value-of select="//ns0:RequestId"/>
      </RequestId>
      <TransactionId>
        <xsl:value-of select="ns0:TenderId"/>
      </TransactionId>
     </TransactionStarted>
  </xsl:template>
   </xsl:stylesheet>

So Here is the two questions I have,

  1. For the current scenario I'm getting correct result but, is there any better way to achieve this?
  2. For some of the incoming request, I want to select template based on external parameter, how do I achieve that?

Update: More clarification on second question,

e.g: In 2nd Incoming request I might have TenderUpdated instead of TenderCreated and for that I want to translate that into either TransactionUpdated or TransactionCanceled depends on external string parameter.

so If incoming request is like,

<?xml version="1.0" encoding="utf-8"?>
<Response xmlns="http://www.example.com/api">
  <SourceId>SourceId1</SourceId>
  <RequestId>RequestId1</RequestId>
   <TenderUpdated>
    <TenderId>TenderId1</TenderId>
  </TenderUpdated>
 </Response>

And parameter passed is Update, translate to

<?xml version="1.0" encoding="utf-8"?>
    <TransactionUpdated xmlns="http://www.example1.com/api">
      <RequestId>RequestId1</RequestId>
      <TransactionId>TenderId1</TransactionId>
      <Update/>
     </TransactionUpdated>

And if parameter passed is Cancel , translate to

   <?xml version="1.0" encoding="utf-8"?>
        <TransactionCanceled xmlns="http://www.example1.com/api">
          <RequestId>RequestId1</RequestId>
          <TransactionId>TenderId1</TransactionId>
          <Cancel/>
         </TransactionCanceled>

This is simplified scenario, actual message have more xml tag and TransactionUpdated and TransactionCanceled differs much.

5
  • With the input root having <Request xmlns="http://www.example.com/api"> I don't see why your stylesheet would do what you say it does as neither StatusEvent nor RequestId select or match elements in the input namespace, they select or match elements in no namespace. Commented Oct 23, 2015 at 16:16
  • I simplified the scenario so might have missed bit, making that namespace default would work, right? Commented Oct 23, 2015 at 16:20
  • 1
    With an XSLT 2.0 processor like Saxon 9 you could set xpath-default-namespace="http://www.example.com/api" but with .NET's XslCompiledTransform you would need to use the prefix ns0 you declared on all your element names e.g. ns0:StatusEvent. Commented Oct 23, 2015 at 16:29
  • @Martin - Thanks, Updated xslt to have element prefixed with namespace. Commented Oct 23, 2015 at 16:44
  • As for the second question, please explain what kind of value (string, number, boolean, node set) the parameter is supposed to have and how you want to use, i.e. how the parameter determines which template to use. Commented Oct 23, 2015 at 17:03

1 Answer 1

1

If you know all result elements should be in the namespace http://www.example1.com/api then you can put that on the xsl:stylesheet e.g.

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns="http://www.example1.com/api"
    xmlns:ns0="http://www.example.com/api"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="ns0 xs">

As for the parameter, declare it as

<xsl:param name="transactionName" select="'Updated'"/>

and when you want to create an element using that parameter don't use a literal result element but xsl:element instead:

<xsl:element name="Transaction{$transactionName}">...</xsl:element>

Unfortunately in XSLT 1.0 the use of parameter or variables references inside of patterns is not allowed so to handle the condition you can only write a template matching on the element name and then you need to use xsl:choose/xsl:when to handle the different element names. Here is an example that you can hopefully extend:

<xsl:transform
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0"
    xmlns:api="http://www.example.com/api"
    xmlns="http://www.example1.com/api"
    exclude-result-prefixes="api">

    <xsl:param name="transactionName" select="'Update'"/>

    <xsl:output indent="yes"/>

    <xsl:template match="api:Response">
        <xsl:element name="Transaction{$transactionName}">
            <xsl:apply-templates select="api:RequestId | api:TenderUpdated/api:TenderId"/>
            <xsl:choose>
                <xsl:when test="$transactionName = 'Update'">
                    <Update/>
                </xsl:when>
                <xsl:when test="$transactionName = 'Cancel'">
                    <Cancel/>
                </xsl:when>
            </xsl:choose>
        </xsl:element>
    </xsl:template>

    <xsl:template match="api:RequestId">
        <RequestId>
            <xsl:apply-templates/>
        </RequestId>
    </xsl:template>

    <xsl:template match="api:TenderId">
        <TransactionId>
            <xsl:apply-templates/>
        </TransactionId>
    </xsl:template>
</xsl:transform>

Online at http://xsltransform.net/94rmq5R.

If there are lots of differences between the input formats then I might be tempted to handle them by different stylesheets. If that is not possible then it might make sense to branch in the template for the root and use modes on templates to distinguish the handling e.g.

    <xsl:template match="api:Response">
            <xsl:choose>
                <xsl:when test="$transactionName = 'Update'">
                    <xsl:apply-templates select="." mode="update"/>
                </xsl:when>
                <xsl:when test="$transactionName = 'Cancel'">
                    <xsl:apply-templates select="." mode="cancel"/>
                </xsl:when>
            </xsl:choose>
        </xsl:element>
    </xsl:template>

    <xsl:template match="api:Response" mode="update">
       <TransactionUpdate>
         <xsl:apply-templates select="api:Foo | api:Bar" mode="update"/>
         <Update/>
       <TransactionUpdate>
    </xsl:template>
    <!-- now add templates for the other elements and for other mode(s) here -->
Sign up to request clarification or add additional context in comments.

5 Comments

body of TransactionUpdated or TransactionCanceled might be different so instead of defining element once and just change the name, can we make separate template and choose based on parameter passed?
Can you edit your question and show the two different elements and the two different results you want to create based on the parameter?
I've updated my question, just curious can we do something like <xsl:template match="$transactionName = 'Updated'"> to find template based on parameter in Xslt version 1.0 ?
@Glk, I have edited my answer with some more suggestions to address the edits you have made to your question and to address your comments.
I ended up using Branching using mode and also creating element name on the fly worked for other purpose. Thank you

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.