Tuesday, June 24, 2014

Base64 encode/decode part of messages in map - part 2

Back in November I wrote a post about encoding messages as base64 strings in a BizTalk map. I never added the explicit implementation of how to decode the message though. However, I was asked to provide an example of it, so here is part 2: how to decode a base64 string from an XML message and output it in BizTalk.

Consider the scenario to be the opposite of what we did in part 1.

We have a message on the input side that has two fields, one with an id as a string, and another string element that holds the entire base64 encoded string.

<ns0:Root xmlns:ns0="http://BizTalk_Server_Project6.Schema1">
 
<SomeId>1</SomeId>
 
<EncodedData>PG5zMDpSb290IHhtbG5zOm5zMD0iaHR0cDovL0JpelRhbGtfU2VydmVyX1Byb2plY3Q2LlNjaGVtYTIiPg0KICA8RGF0YT5EYXRhXzA8L0RhdGE+IA0KICA8RGF0YTI+RGF0YTJfMDwvRGF0YTI+IA0KPC9uczA6Um9vdD4=</EncodedData>

</ns0:Root>

On the output side, we want to have a message that conforms to the schema we have defined. The message is the entire contents of the base64 encoded string in the input.

If we parse the encoded string, we get:
<ns0:Root xmlns:ns0="http://BizTalk_Server_Project6.Schema2">
 
<Data>Data_0</Data>
 
<Data2>Data2_0</Data2>
</ns0:Root>

Which conforms fully to the schema defined.

So, the task is to extract the contents from the EncodedData element, decode them, and use the full string as the output message. All in a single BizTalk map.

The map will look like this to begin with, with the two schemas chosen:


Similar to when we encoded, we add first a scripting functoid that has no connections, and paste the code for the decode function in it:

public string Base64DecodeData(string param1)
{
            byte[] decodedBytes = Convert.FromBase64String(param1);
            return System.Text.UTF8Encoding.UTF8.GetString(decodedBytes);
}


We simply take a string as an input parameter, decode this and return the decoded string back.

Then we create a new scripting functoid that we set to inline Xsl and paste this code into it:

<xsl:variable name="data">
 
<xsl:value-of select="//EncodedData" />
</xsl:variable>
<xsl:value-of select="userCSharp:Base64DecodeData($data)" disable-output-escaping="yes" />

This functoid is connected to the output root node giving us a map that looks like this:


When executing this map with the input message above, we will get the output message properly formatted.

The tricks used here is two. First off, we use the same pattern as before with calling a predefined function we have in another scripting functoid in order to call a simple C# function from our XSL code. Then in the XSL, we first extract the string from our input message and store it in a variable. This is then passed into our C# function and we get a string back. However, if we do not specify the disable-output-escaping="yes" in our value-of select, we would get the string entity encoded. With this extra property set, the string will be output as it is and the way we want it.

The same technique can of course easily be used to just output part of a message by simply connecting the scripting functoid to the node you want to populate (if for instance you have a schema that has a node defined as xs:any that you want to use).

5 comments:

  1. Thanks Marcus, this is just what I need!

    ReplyDelete
  2. Hi Marcus,

    I managed to decode XML message with help of your example. In my case the same source message may also have an element having base64 encoded zip file attachment. Do you have any idea how that could be decoded and saved to a destination folder?

    Regards,

    Teemu

    ReplyDelete
    Replies
    1. In that case, I'd probably send the base64-encoded string as it is to an element of type xs:string. Then in a pipeline component, extract the element content, decode it and output the binary stream as the message body and save it to disk.

      Delete
  3. Thanks for a quick respone. Maybe Saravana's approach (http://blogs.biztalk360.com/dealing-with-web-services-returning-byte-in-biztalk/) would work also? In that case I would use a pipeline component just for unzipping attachments.

    ReplyDelete
    Replies
    1. Ah yes, certainly. Depending on your preferences, you can easily just create an orchestration and extract the encoded string there. Then pass it on (or the entire message for that matter) to a helper class that decodes and gives back a binary object. Then you send this object out as a message to the messagebox and route it to the destination as usual.

      It's all a matter of preference and whether the orchestration contains more logic that is needed. I myself try to keep to pure messaging as much as possible hence the suggestion of doing it in the pipeline.

      Delete