Extracting BizTalk Messages Content using XPath in Custom Pipeline Components
Introduction
You find yourself writing custom pipeline components in nearly all BizTalk related projects. So this is a common activity. And usually it will mean eventually you will need to extract data from the message coming on the wire using XPath statements.
Problem
The issues you usually face while trying to extract data from BizTalk massages is that the message stream would not seekable. And another problem is how to do this data extraction with the minimal memory footprint (no XmlDocument L) and also as fast as possible.
Solution
I created mainly two functions that I use commonly for these tasks. The first one is to get the stream from the message and create a seekable one to be used later on.
private Stream GetMessageStream(Microsoft.BizTalk.Message.Interop.IBaseMessage msg, Microsoft.BizTalk.Component.Interop.IPipelineContext context)
{
Stream stream = msg.BodyPart.GetOriginalDataStream();
if (!stream.CanSeek)
{
ReadOnlySeekableStream readStream = new ReadOnlySeekableStream(stream);
if (context != null)
{
context.ResourceTracker.AddResource(readStream);
}
msg.BodyPart.Data = readStream;
stream = readStream;
}
return stream;
}
The second method is the one that would perform the data extraction as follows.
private string ExtractDataValueXPath(Stream MsgStream, string MsgXPath)
{
XmlReaderSettings settings = new XmlReaderSettings()
{
ConformanceLevel = ConformanceLevel.Document,
IgnoreWhitespace = true,
ValidationType = ValidationType.None,
IgnoreProcessingInstructions = true,
IgnoreComments = true,
CloseInput = false
};
MsgStream.Seek(0, SeekOrigin.Begin);
XmlReader reader = XmlReader.Create(MsgStream, settings);
string strValue = null;
if (!string.IsNullOrEmpty(MsgXPath))
{
if (reader.Read())
{
XPathDocument xPathDoc = new XPathDocument(reader);
XPathNavigator xNavigator = xPathDoc.CreateNavigator();
XPathNodeIterator xNodes = xNavigator.Select(MsgXPath);
if (xNodes.Count != 0 && xNodes.MoveNext())
{
strValue = xNodes.Current.Value;
}
MsgStream.Seek(0, SeekOrigin.Begin);
}
}
return strValue;
}
As you can see I am using XPathDocument with a XmlReader to perform this as fast as possible.