Updating repeating nodes in an XML Document with the same value using XPathMutatorStream

Posted: May 31, 2008  |  Categories: BizTalk Uncategorized

 It’s been a while since I’ve posted some BizTalk coding tidbit… Busy times!

 I often have to set the value of an element under a message’s repeating node with the same value. Of course, normally you’d use a map, but what if the value isn’t in the source message? The last time I ran into this was in a project where orders coming into BizTalk via a web service had to be inserted into an AS400 system. The way the already existing AS400 tables were designed to link the order header and the order details was to have the same GUID inserted into both tables, so the order items schema had a GUID field for each line of the order.

 I couldn’t use a distinguished field to set the value of the GUID in the order lines schema. This is, of course, because it’s under a repeating node. Even if you have a second message with the the value (say, for example, you use the SQL Adapter and a stored procedure to insert the order header and it returns the newly generated ID in a message), you would then have to use a map with multiple inputs… So it ocurred to me that this could be a great opportunity for the XPathMutatorStream class! This class is usually used in custom pipeline components. It has been blogged about by Martijn Hoogendoorn and Patrick Wellink amongst others and I wanted to share how I used it. Say we have the following schemas and map:

XPathMutatorStream Map

And we make sure the WEBGUID element on the destination schema is populated with a blank value by selecting it and setting its “Value” property to <empty> (this will create the element with an empty value on the destination message):

 Then, I created the following helper c# class library to perform the update. Don’t forget to reference Microsoft.Biztalk.Streaming and Microsoft.BizTalk.XPathReader (get them from the GAC). 

using System;

using System.Xml;

using System.IO;

using Microsoft.BizTalk.XPath;

using Microsoft.BizTalk.Streaming;

 

namespace MyCompanyUtilities

{

    [Serializable]

    public class XPathUtil

    {

 

        private string newValue = “”;

  

        public string NewValue

        {

            get { return newValue; }

            set { newValue = value; }

        }

 

        private string xpathToUpdate = “”;

 

        public string XpathToUpdate

        {

            get { return xpathToUpdate; }

            set { xpathToUpdate = value; }

        }

 

        //Updates an xml document with all matches in an xpath with a value

        public XmlDocument updateWithXpath(XmlDocument xDoc)

        {

            //Check for valid arguments

            if (newValue == “”)

                throw new ArgumentException(“NewValue”);

            if (xpathToUpdate == “”)

                throw new ArgumentException(“XpathToUpdate”);

 

            XmlDocument retXml = new XmlDocument();

 

            //Setup the stream and rewind

            using (MemoryStream memstream = new MemoryStream())

            {

                //Copy xml document to memory stream and rewind

                xDoc.Save(memstream);

                memstream.Position = 0;

 

                //Define the XPathMutator

                XPathCollection queries = new XPathCollection();

                queries.Add(new XPathExpression(xpathToUpdate));

                ValueMutator xpathMatcher = new ValueMutator(this.XPathCallBack);

 

                //Get resulting stream into response xml

                retXml.Load(new XPathMutatorStream(memstream, queries, xpathMatcher));

            }

 

            return retXml;

        }

 

        private void XPathCallBack(int matchIdx, XPathExpression matchExpr, string origVal, ref string finalVal)

        {

            finalVal = newValue;

        }

    }

}

 So from there it was just a case of referencing the c# class library from my BizTalk solution, and calling the helper class from the orchestration in a construct shape (in the same construction shape as the map). Here’s how the sample orchestration looks like:

XPathMutatorStream Orchestration

  I created a variable in the orchestration for the class above. The “Set GUID” construct shape has the following: 

xpathHelper = new MyCompanyUtilities.XPathUtil();

xpathHelper.XpathToUpdate = “/*[local-name()=’Root’ and namespace-uri()=’http://MyCompany.OrderItems’]/*[local-name()=’TBWEBITEM’ and namespace-uri()=”]/*[local-name()=’WEBGUID’ and namespace-uri()=”]”;

xpathHelper.NewValue = msg_WebOrder(BTS.MessageID);

msg_OrderItems = xpathHelper.updateWithXpath(msg_OrderItems);

  So when I input the following message: 

<ns0:Root xmlns:ns0=http://MyCompany.WebOrder>

  <WebOrder>

    <OrderHeader>

      <OrderNumber>1</OrderNumber>

    </OrderHeader>

    <Items>

      <Item>

        <ItemNumber>23</ItemNumber>

        <ItemDescription>Cheese</ItemDescription>

        <Quantity>10</Quantity>

        <UnitPriceExcTax>5.4</UnitPriceExcTax>

      </Item>

      <Item>

        <ItemNumber>14</ItemNumber>

        <ItemDescription>Ham</ItemDescription>

        <Quantity>10</Quantity>

        <UnitPriceExcTax>4.0</UnitPriceExcTax>

      </Item>

      <Item>

        <ItemNumber>12</ItemNumber>

        <ItemDescription>Bread</ItemDescription>

        <Quantity>1</Quantity>

        <UnitPriceExcTax>3.30</UnitPriceExcTax>

      </Item>

    </Items>

  </WebOrder>

</ns0:Root>

 The output message is: 

<ns0:Root xmlns:ns0=http://MyCompany.OrderItems>

  <TBWEBITEM>

    <WEBGUID>d181486e-649b-4d29-87ab-a811bb175ef3</WEBGUID>

    <WEBITMNBR>23</WEBITMNBR>

    <WEBITMDSC>Cheese</WEBITMDSC>

    <WEBQTY>10</WEBQTY>

    <WEBPRC>5.4</WEBPRC>

  </TBWEBITEM>

  <TBWEBITEM>

    <WEBGUID>d181486e-649b-4d29-87ab-a811bb175ef3</WEBGUID>

    <WEBITMNBR>14</WEBITMNBR>

    <WEBITMDSC>Ham</WEBITMDSC>

    <WEBQTY>10</WEBQTY>

    <WEBPRC>4.0</WEBPRC>

  </TBWEBITEM>

  <TBWEBITEM>

    <WEBGUID>d181486e-649b-4d29-87ab-a811bb175ef3</WEBGUID>

    <WEBITMNBR>12</WEBITMNBR>

    <WEBITMDSC>Bread</WEBITMDSC>

    <WEBQTY>1</WEBQTY>

    <WEBPRC>3.30</WEBPRC>

  </TBWEBITEM>

</ns0:Root>

 So the helper class updated all the WEBGUID elements with the same value without us having to use a map with multiple inputs, or performing a loop inside the orchestration, or browsing through the message using XpathNavigator. Of course you can use the XPathMutatorStream with any repeating node you want or with a list of different xpath queries that will match somewhere in the message. You can also add some funky logic in the XPathCallBack method to have it use different values depending on the original value.

Dowload the sample here.

Note: I could have also used a custom pipeline functoid to get the Message ID from the source schema by using the “Custom GetContextProperty functoid“.

1 thought on “Updating repeating nodes in an XML Document with the same value using XPathMutatorStream”

  1. Hi
    I am trying to create a biztalk map that will take the following XML:

    Andile
    SA

    Box 234
    34 Mowbury

    and output the following:

    Andile
    SA
    Box 234

    Andile
    SA
    34 Mowbury

    I need help with this as It does not seem to be working

Comments are closed.

BizTalk360
BizTalk Server

Over 500+ customers across
30+ countries depend on BizTalk360

Learn More
Serverless360
Azure

Manage and monitor serverless
components effortlessly

Learn More
Atomicscope
Business Users

Monitor your Business Activity in iPaaS
or Hybrid integration solutions

Learn More

Back to Top