Windows Azure BizTalk Services is a cloud-based integration service that provides Business-to-Business (B2B) and Enterprise Application Integration (EAI) capabilities for delivering cloud and hybrid integration solutions. With the latest release of Azure BizTalk Services, only the XML and text content-types are supported.

JSON, on the other hand, is getting ubiquitous, so this blog post describes on how to add support for JSON by extending the Azure BizTalk Bridges using the message inspectors. Some of the use cases might be interacting with a JSON-only endpoint like Azure Mobile Services, or for clients only interested in communicating via a JSON payload.

Scenario: A Message is published to an Azure BizTalk service http endpoint, based on the content type (XML/JSON) the message should be routed to its corresponding bridge, then converted to either JSON or XML, and finally inserted into their respective service bus queues.

Architecture:

Fig. 1: High-Level Overview
Fig. 2: Itinerary
  1. An incoming XML message will be converted to a JSON message and placed in JSON service bus queue.
  2. An incoming JSON message will be converted to an XML message and will be placed in to an XML service bus queue.

Message Inspectors: These are one of the extensibility points of BizTalk bridges where we can inject custom code in to the pipeline stage’s On Enter or On Exit methods. For this scenario we created two classes: JSONToXMLMessageConverter and XMLToJSONMessageConverter. The entire solution is available to download from GitHub including unit tests.

JSON to XML conversion:  This class implements the IMessageInspector class and exposes three properties root node, namespace and the prefix to use for new XML message, the Execute method of the interface uses Newtonsoft JSON library to convert the incoming JSON to XML message.

XML to JSON conversion:  Similar to the previous class, we implement the IMessageInspector and expose a property called type name. This is the class name that we will use to convert the XML to JSON using the method JSONConvert.SerializeObject of the Newtonsoft JSON library.

Fig. 3: Properties for JSON to XML message inspector
Fig. 4: Properties for XML to JSON message inspector

The following video demonstrates the working sample:

The following are the code snippets for the conversion classes:

JsonToXmlConverter.cs

public class JsonToXmlConverter : IMessageInspector
    {
        [PipelineProperty(Name = "RootNode")]
        public string RootNode { get; set; }

        [PipelineProperty(Name = "Namespace")]
        public string Namespace { get; set; }

        [PipelineProperty(Name = "Prefix")]
        public string Prefix { get; set; }

        private const string ApplicationXml = "application/xml";
        private const string ApplicationJson = "application/json";

        public Task Execute(IMessage message, IMessageInspectorContext context)
        {
            context.Tracer.TraceEvent(TraceEventType.Information,
                                      String.Format("JsonToXmlConverter - MessageType: {0}", message.ContentType));

            return Task.Factory.StartNew(() =>
                {
                    message.Data = GetXmlStream(message.Data, message.ContentType);
                    message.Data.Position = 0;
                    message.ContentType = new ContentType(ApplicationXml);
                    context.Tracer.TraceEvent(TraceEventType.Information, "JsonToXmlConverter - json to xml conversion done.");
                });
        }

        private Stream GetXmlStream(Stream msgStream, ContentType contentType)
        {
            Stream originalStream = msgStream;
            string json = null;

            if (contentType.ToString().Contains(ApplicationJson))
            {
                using (var reader = new StreamReader(originalStream))
                {
                    json = reader.ReadToEnd();
                }
            }
                // When Bridges are chained, WABS converts the message into a base64 string and sends it to the next bridge and sets the message content type to application/xml
                // the following c
            else if (contentType.ToString().Contains(ApplicationXml))
            {
                //<Binary>ew0KICAicGVyc29uIjogew0KICAgICJuYW1lIjogIm5lbiIsDQogICAgInVybCI6ICJuZW5AMTIzNCINCiAgfQ0KfQ==</Binary>
                var parser = new XmlDocument();
                parser.Load(msgStream);

                string jsonInnerXml = parser.FirstChild.InnerXml;
                byte[] data = Convert.FromBase64String(jsonInnerXml);
                json = Encoding.ASCII.GetString(data);

            }

            //// https://www.modhul.com/2013/04/30/restfully-getting-json-formatted-data-with-biztalk-2013/

            var xmlDoc = new XmlDocument();
            XmlNode jsonNode = JsonConvert.DeserializeXmlNode(json, "RootNode");
            XmlNode rootNode = xmlDoc.CreateNode(XmlNodeType.Element, Prefix, RootNode, Namespace);
            var selectSingleNode = jsonNode.SelectSingleNode("RootNode");
            if (selectSingleNode != null)
                rootNode.InnerXml = selectSingleNode.InnerXml;
            xmlDoc.AppendChild(rootNode);
            string innerXml = xmlDoc.InnerXml;

            byte[] output = Encoding.ASCII.GetBytes(innerXml);
            var memoryStream = new MemoryStream();
            memoryStream.Write(output, 0, output.Length);
            memoryStream.Position = 0;

            return memoryStream;
        }

    }

XmlToJsonConverter.cs

public class XmlToJsonConverter : IMessageInspector
    {
        [PipelineProperty(Name = "TypeName")]
        public string TypeName { get; set; }

        public Task Execute(IMessage message, IMessageInspectorContext context)
        {
            context.Tracer.TraceEvent(TraceEventType.Information,
                                     String.Format("XmlToJsonConverter - TypeName: {0}", TypeName));
            return Task.Factory.StartNew(() =>
            {
                message.Data = GetJsonStream(message.Data);
                message.Data.Position = 0;
                message.ContentType = new ContentType("application/json");
                context.Tracer.TraceEvent(TraceEventType.Information,
                                        String.Format("XmlToJsonConverter -  xml to json conversion completed."));
            });
        }

        public Stream GetJsonStream(Stream msgStream)
        {
            Stream originalStream = msgStream;
            Type myClassType = Type.GetType(TypeName);
            object reqObj = FromXml(originalStream, myClassType);
            string jsonText = JsonConvert.SerializeObject(reqObj, myClassType, Formatting.None,
                                                          new JsonSerializerSettings());
            byte[] outBytes = Encoding.ASCII.GetBytes(jsonText);

            var memStream = new MemoryStream();
            memStream.Write(outBytes, 0, outBytes.Length);
            memStream.Position = 0;

            return memStream;
        }

        //Creates an object from an XML string.
        public static object FromXml(Stream xml, Type objType)
        {
            var ser = new XmlSerializer(objType);
            return ser.Deserialize(xml);
        }
    }