Tech Blog

BizUnit: XmlValidationStep using .NET 2.0 Schema Validation

This is something that bit me recently:
The current version of BizUnit (v.2.2.1003.0) 
uses the .NET 1.0 XmlValidatingReader to
do schema validation in both the XmlValidationStep and XmlValidationStepEx steps.

.NET 2.0 deprecated the XmlValidatingReader class, proposing that you use the XmlReader class
and the new XmlReaderSettings class
instead.
Microsoft also introduced a slew of bug fixes to the validation logic – including
the ability to correctly process Xsd import and include directives.

I was trying to test a fairly complex schema recently with BizUnit – a schema which
has a lot of includes.
And BizUnit would consistently throw an exception.
Looking through the BizUnit source code, I noticed that the XmlValidatingReader was
being used:
XmlDocument doc
=
new XmlDocument();
XmlTextReader trData
=
new XmlTextReader(
data );
XmlValidatingReader vr = new XmlValidatingReader(trData);

// If schema was specified use it to validate against…

if ( null != xmlSchemaPath
)
{
    MemoryStream xsdSchema = StreamHelper.LoadFileToStream(xmlSchemaPath);
    XmlTextReader trSchema = new XmlTextReader(
xsdSchema );
    XmlSchemaCollection xsc = new XmlSchemaCollection();

    if ( null !=
ns )
    {
        xsc.Add( ns, trSchema );
        vr.Schemas.Add(xsc);
    }

    doc.Load(vr);
}

I’ve raised an issue on the BizUnit CodePlex site for this, but in the meantime I
updated the XmlValidationStep.cs source file to this:


XmlDocument doc
= new
XmlDocument();
XmlTextReader trData
= new
XmlTextReader(data);

// If schema was specified use it
to vaidate against…


if (null != xmlSchemaPath)
{
    FileInfo fi = new FileInfo(xmlSchemaPath);
    // Store the current diretcory whilst we
temporarily change it


    string currentDirectory = Environment.CurrentDirectory;
    // Change the current directory to the schema
location so that
    // any relative imports/includes in the schema will work correctly


    Environment.CurrentDirectory = fi.DirectoryName;
    MemoryStream xsdSchema = StreamHelper.LoadFileToStream(xmlSchemaPath);
    XmlTextReader trSchema = new XmlTextReader(xsdSchema);

    XmlReaderSettings settings = new XmlReaderSettings();
    settings.ValidationType = ValidationType.Schema;

    if (null !=
ns)
    {
        settings.Schemas.Add(ns, trSchema);
    }
    else

    {
        settings.Schemas.Add(null,
trSchema);
    }

    XmlReader vr = XmlReader.Create(trData,
settings);
    while (vr.Read()) { }

    // Reset the current directory

    Environment.CurrentDirectory = currentDirectory;
}


Note: Interestingly, the BizTalk XmlValidator pipeline component also uses
the .NET 1.0 classes…

I also made two other changes:

  1. There’s a bug (well, I see it as a bug!) in the current XmlValidationStep.
    When XmlDocument.SelectSingleNode is called, and an XmlNode returned,
    the code doesn’t check if the returned node is null before trying to access the InnerText property
    on the node:


    XmlNode checkNode = doc.SelectSingleNode( xpathExp );

    if ( 0 != nodeValue.CompareTo( checkNode.InnerText )
    )

    If the xPath statement you’re using returns no nodes (usually because you typed it
    in wrong!), then this will throw an exception.
    Your test will fail (as expected) but instead of a useful error, you’ll get an “object
    reference not set to an instance of an object
    ” error…

  2. It’s always seemed a bit strange to me that there is both an XmlValidationStep and
    an XmlValidationStepEx – one uses XmlDocument.SelectSingleNode, the other uses an
    XPathNavigator. I’m sure there’s a good reason for it.
    Statements such as count(.) will
    throw an XPathException() with SelectSingleNode,
    but will work correctly with an XPathNavigator.
    So I combined both approaches in my XmlValidationStep: SelectSingleNode is tried first.
    If this throws an exception, then an XPathNavigator is used:

    XmlNode checkNode = null;
    try

    {
        checkNode = doc.SelectSingleNode(xpathExp);
    }
    catch { }
    if (checkNode != null)
    {
        if (0 != nodeValue.CompareTo(checkNode.InnerText))
        {
           throw new ApplicationException(string.Format(“XmlValidationStepEnhanced
    failed, compare {0} != {1}, xpath query used: {2}”
    , nodeValue, checkNode.InnerText,
    xpathExp));
        }
    }
    else

    {
        XPathNavigator xpn = doc.CreateNavigator();
        object result = xpn.Evaluate(xpathExp);

        string checkValue = null;
        if (result.GetType().Name == “XPathSelectionIterator“)
        {
            XPathNodeIterator xpi
    = result as XPathNodeIterator;
            xpi.MoveNext();
            if (null !=
    xpi)
            {
                checkValue = xpi.Current.ToString();
            }
        }
        else

        {
            checkValue = result.ToString();
        }

        if (0
    != nodeValue.CompareTo(checkValue))

    Works for me!

Not sure if I should do this (!) but here’s a version of my updated XmlValidationStep.cs file.
If you wish you can grab the source file and compile your own version of the core
BizUnit assembly should you need this functionality as well.

   XmlValidationStep.cs


Back to Tech Blog