BizTalk: Preserve XML Header When Debatching

Recently on the Forum, a poster asked about techniques to debatch an XML message while preserving a header element in the individual messages.  Unlike the Flat File Disassembler, there is no concept of Preserve Header on the XmlDisassembler.

A quick Bing on the topic will result in many varying suggestions from Custom Pipeline Components to XPath debatching and others.  While all of these will likely work, this problem is actually pretty easy to solve with just one extra Schema and Map.

Consider the following input message:

<ns0:IncomingMessage xmlns:ns0="http://DebatchXmlWithHeader.MessageWithHeader">
  <Header>
    <Source>Babylon5</Source>
    <Identifier>PlanetList</Identifier>
  </Header>
  <Details>
    <Name>Mercury</Name>
    <Year>2283</Year>
  </Details>
  <Details>
    <Name>Mars</Name>
    <Year>2295</Year>
  </Details>
  <Details>
    <Name>Jupiter</Name>
    <Year>2315</Year>
  </Details>
</ns0:IncomingMessage>

The Header information is relevant to all of the Details records.  In our app, we would like to process each Details separately, but still require the Header information.  So, we would like a debatch output like this:

<ns0:IncomingMessage xmlns:ns0="http://DebatchXmlWithHeader.MessageWithHeader">
    <Header>
        <Source>Babylon5</Source>
        <Identifier>PlanetList</Identifier>
    </Header>
    <Details>
        <Name>Mercury</Name>
        <Year>2283</Year>
    </Details>
</ns0:IncomingMessage>

The sample code for this article can be found in the MSDN Code Gallery at BizTalk Sample: Preserve XML Header When Debatching.

Restructuring the Message

Defining the Schema

The first step getting out desired output is restructure the incoming message to associate an instance of the Header element with each instance of the Details element.  This requires an intermediate Schema defining a Message that can hold multiple IncomingMessage elements.

The IncomingMessageWrapper Schema itself only defines one Element, the header Element IncomingMessageWrapper itself.  IncomingMessage is defined by an xs:import from the original MessageWithHeader.xsd.  By importing the IncomingMessage definition, we don’t have to duplicate the structure and wrapped message will automatically be in sync with the base message.

With the <Schema> node selected, set the Envelope Property to Yes.  This tells the XmlDisassembler treat this as an Envelop Schema and process and body_xpath flags.

With the root IncomingMessageWrapper Element selected, set the Body XPath property to the IncomingMessageWrapper Element itself.  This tells the XmlDisassembler that everything under IncomingMessageWrapper will be debatched into individual documents.

Creating the Map

The restructuring of the message to create a 1:1 Header/Details relationship is done with a Map.

The Map uses a Looping Functoid connecting the source Details to the destination IncomingMessage Element.  This causes the Mapper to loop on Details creating an instance of IncomingMessage for each Details.  The output of this map is a collection of IncomingMessage elements with one Details and the Header.

<ns1:IncomingMessageWrapper xmlns:ns1="http://DebatchXmlWithHeader.IncomingMessageWrapper" xmlns:ns0="http://DebatchXmlWithHeader.MessageWithHeader">
    <ns0:IncomingMessage>
        <Header>
            <Source>Babylon5</Source>
            <Identifier>PlanetList</Identifier>
        </Header>
        <Details>
            <Name>Mercury</Name>
            <Year>2283</Year>
        </Details>
    </ns0:IncomingMessage>
    <ns0:IncomingMessage>
        <Header>
            <Source>Babylon5</Source>
            <Identifier>PlanetList</Identifier>
        </Header>
        <Details>
            <Name>Mars</Name>
            <Year>2295</Year>
        </Details>
    </ns0:IncomingMessage>
    <ns0:IncomingMessage>
        <Header>
            <Source>Babylon5</Source>
            <Identifier>PlanetList</Identifier>
        </Header>
        <Details>
            <Name>Jupiter</Name>
            <Year>2315</Year>
        </Details>
    </ns0:IncomingMessage>
</ns1:IncomingMessageWrapper>

 

Also important to note is that this technique can be used to duplicate any node, including trailers, in the debatched messages.

 

Debatching the Output

Since we now have a Message XML that is a collection of properly formatted IncomingMessage elements, all we have to do is process this message with the built in standard XmlDisassmbler.  For this, there are several options.

Orchestration

The sample project includes an Orchestration that performs the entire process and is an example of how to execute a Receive Pipeline in an Orchestration.

Pipelines

Because this solution involves only Schemas, Maps and the XmlDisassembler, it can all be done in Pipelines.  No additional components are necessary.

The sample project includes bindings for both the Orchestration and Pipeline only implementations.

Output

Routing the debatched output to a folder, we open one of the Xml messages.

    <ns0:IncomingMessage>
        <Header>
            <Source>Babylon5</Source>
            <Identifier>PlanetList</Identifier>
        </Header>
        <Details>
            <Name>Jupiter</Name>
            <Year>2315</Year>
        </Details>
    </ns0:IncomingMessage>

The individual message includes the XML Header element as expected.

Conclusion

While a built in option so retain headers and trailers in XML messages would be convenient, it is still an easily achievable goal.  Additionally, this solution is entirely flexible and can be modified to suit any specific scenario beyond headers and trailers.

See Also

Another important place to find a huge amount of BizTalk related articles is the TechNet Wiki itself. The best entry point is BizTalk Server Resources on the TechNet Wiki.