woensdag 7 maart 2012

Remove Empty Nodes

We found out that Navision does not like empty nodes when receiving an XML message, as it places the empty elements in it's inbound table with empty strings, which means that validation for these elements will fail. To solve this, we had to remove the empty elements from the message that is being sent to Navision. A custom pipeline component was created for this, with the following Execute method.
/// <summary>
/// IComponent.Execute method is used to initiate the processing of the 
/// message in this pipeline component.
/// </summary>
/// <param name="pc">Pipeline context.</param>
/// <param name="inmsg">Input message.</param>
/// <returns>Original input message.</returns>
public Microsoft.BizTalk.Message.Interop.IBaseMessage Execute(
    Microsoft.BizTalk.Component.Interop.IPipelineContext pc, 
    Microsoft.BizTalk.Message.Interop.IBaseMessage inmsg)
{
    // Remove empty elements from the message
    return RemoveEmptyElements(inmsg);
}

Now add the method that will remove the empty elements from the message that is being processed.

/// <summary>
/// Removes empty elements from a BizTalk message.
/// </summary>
/// <param name="pInMessage">The message being processed in the pipeline.
</param>
/// <returns>The message with the empty elements removed.</returns>
private IBaseMessage RemoveEmptyElements(IBaseMessage pInMessage)
{
    // To be able to use XSLT, we need the xsi namespace
    XmlDocument xmlDocument = AddXsiNameSpace(inmsg.BodyPart.Data)
    
    // Execute XSLT to do the removing of the empty elements
    Utils.RemoveEmptyNodesXSLT(xmlDocument);
    
    // Create a new body in the message
    pInMessage.BodyPart.Data = new MemoryStream();
    
    // Save the message without the empty elements to the body
    xmlDocument.Save(pInMessage.BodyPart.Data);
    
    // Go to the start of the message, or it will not be processed
    pInMessage.BodyPart.Data.Position = 0;

    // Return the message without empty elements
    return pInMessage;
}

We use XSLT to find the empty elements, and remove these from the message.
/// <summary>
/// Execute XSLT to remove empty elements from an XML message.
/// </summary>
/// <param name="xmlDocument">The XMLDocument with the message.</param>
/// <returns>The XML message with the empty nodes removed.</returns>
private static XmlDocument RemoveEmptyNodesXSLT(XmlDocument xmlDocument)
{
    // Call XSLT to select the empty nodes
    var nodesToRemove = 
        xmlDocument.SelectNodes("//*[count(child::node() | @*) =0]");

    // Loop through all nodes that should be removed
    foreach (XmlNode nodeToRemove in nodesToRemove)
    {
        // Remove the empty node
        nodeToRemove.ParentNode.RemoveChild(nodeToRemove);
    }

    // Return the XMLDocument with the empty elements removed
    return xmlDocument;
}

Seeing how we need to have the xsi namespace to be able to use XSLT on the message, we will add this namespace to the message.

/// <summary>
/// Add the xsi namespace to a XML document.
/// </summary>
/// <param name="inputStream">Stream with the XML message data.</param>
/// <returns>A XMLDocument with the xsi namespace.</returns>
private static XmlDocument AddXsiNameSpace(Stream inputStream)
{
    // Create a new NameTable
    NameTable nTable = new NameTable();
    
    // Create a namespace manager
    XmlNamespaceManager xmlnsManager = new XmlNamespaceManager(nTable);
    
    // Add the xsi namespace
    xmlnsManager.AddNamespace("xsi",
        "http://www.w3.org/2001/XMLSchema-instance");
    
    // Used to parse the XML message
    XmlParserContext context = new XmlParserContext(null, xmlnsManager,
        null, XmlSpace.None);
        
    // Create new SML reader settings
    XmlReaderSettings settings = new XmlReaderSettings();
    
    // Create a new XML reader that reads the XML message that was provided 
    // and adds the xsi namespace
    XmlReader reader = XmlReader.Create(inputStream, settings, context);

    // Create a new XML document that will hold the new XML message
    XmlDocument xmldocument = new XmlDocument();
    
    // Load the message into the document
    xmldocument.Load(reader);
    
    // Return the XMLDocument with the new XML message
    return xmldocument;
}

Geen opmerkingen:

Een reactie posten