Tag Archives: namespace

.NET XML Validation Pitfall

XML Schema validation for XML documents using .Net can be a tricky business sometimes. To give you an idea, have a look at the following example.

Example

We have a customer’s details in a XML document: [Customer.xml]

<?xml version="1.0" encoding="utf-8"?>
<customer xmlns="http://bluechaos.be/customers">
  <name>Dirk Vanderbist</name>
  <address>Wetstraat 9</address>
  <city>Brussels</city>
  <country>Belgium</country>
  <businessID>123321</businessID>
</customer>

Furthermore we have a XML Schema to validate the customer against: [Customer.xsd]

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="customer">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="name" type="xs:string"/>
        <xs:element name="address" type="xs:string"/>
        <xs:element name="city" type="xs:string"/>
        <xs:element name="country" type="xs:string"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Who thinks that the customer above is valid according to the schema? Just raise your hand.

For those that spotted the businessID element which is not mentioned in the schema, I have bad news.  The document is considered valid!

Why?

The XML document uses a namespace, i.e. http://bluechaos.be/customers, which is not referenced by the XML schema. Because of that the schema does not see any element it has to validate. So according to the schema the document is valid!

To avoid this situation you should check if the namespace used by the Xml document and Xml schema match.

Codesample

I wrote a small code sample to show how it can be done. The principal logic takes place in the CheckNamespace and CheckStructure methods. The CheckNamespace verifies if the schema has at least something to validate to overcome overlooking the missing namespace like what happened in the example above. The CheckStructure method does the actual schema validation.

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.IO;
using System.Xml.XPath;

namespace Validate
{
    class Program
    {
        private static List<Exception> validationErrors = new List<Exception>();

        static void Main(string[] args)
        {
            if (args.Length == 2)
            {
                // parse and check arguments
                string xmlFile = args[0];
                string xsdFile = args[1];
                if (!File.Exists(xmlFile)) Console.WriteLine("XML file does not exists");
                if (!File.Exists(xsdFile)) Console.WriteLine("XSD file does not exists");

                // validate xml document
                if (Validate(xmlFile, xsdFile))
                {
                    Console.WriteLine("VALID");
                }
                else
                {
                    Console.WriteLine("INVALID");
                }
            }
            else
            {
                ShowUsage(); // show the required arguments
            }

            Console.WriteLine("Press Enter to Exit...");
            Console.ReadLine();
        }

        static private void ShowUsage()
        {
            Console.WriteLine("validate [XmlFile] [SchemaFile]");
            Console.WriteLine("\t [XmlFile] Xml doocument to validadate");
            Console.WriteLine("\t           e.g. input.xml");
            Console.WriteLine("\t [SchemaFile] Xsd schame used to validadate");
            Console.WriteLine("\t           e.g. schema.xsd");
        }

        static private bool Validate(string xmlFile, string xsdFile)
        {

            if (CheckNamespace(xmlFile, xsdFile) && CheckStructure(xmlFile, xsdFile))
            {
                return true;
            }
            else
            {
                return false;
            }

        }

        static private bool CheckNamespace(string xmlFile, string xsdFile)
        {
            // check if namespace match
            XPathNavigator xmlnav = new XPathDocument(xmlFile).CreateNavigator();
            xmlnav.MoveToFollowing(XPathNodeType.Element);
            string documentNamespace = xmlnav.GetNamespace("");

            XPathNavigator xsdnav = new XPathDocument(xsdFile).CreateNavigator();
            xsdnav.MoveToFollowing(XPathNodeType.Element);
            string schemaNamespace = xsdnav.GetNamespace("");

            if (documentNamespace != schemaNamespace)
            {
                Console.WriteLine("Namespace mismatch xml:'{0}'<>xsd:'{1}'",
                 documentNamespace, schemaNamespace);
                return false;
            }
            else
            {
                Console.WriteLine("Namespace match xml:'{0}'==xsd:'{1}'",
                 documentNamespace, schemaNamespace);
                return true;
            }
        }

        static private bool CheckStructure(string xmlFile, string xsdFile)
        {
            // validate xmlFile against schema
            try
            {
                XmlTextReader reader = new XmlTextReader(xmlFile);
                XmlReaderSettings readerSettings = new XmlReaderSettings();
                readerSettings.ValidationType = ValidationType.Schema;
                readerSettings.Schemas.Add(null, xsdFile);
                readerSettings.ValidationFlags = XmlSchemaValidationFlags.None;
                readerSettings.ValidationEventHandler += new ValidationEventHandler(ValidationEvent);

                XmlReader xmlReader = XmlReader.Create(reader, readerSettings);

                while (xmlReader.Read()); //Read through the document
                xmlReader.Close();

            }
            catch (Exception e)
            {
                Console.WriteLine("Exception occured: " + e.Message);
                return false;
            }

            if (validationErrors.Count > 0) return false;
            return true;
        }

        // validate callback event
        static private void ValidationEvent(object sender, ValidationEventArgs ve)
        {
            // add exception to exeception list and dump to console
            validationErrors.Add(ve.Exception);
            Console.WriteLine(ve.Exception);
        }
    }
}

To Conclude

You should remember to check the namespaces if it looks like your XML Schema validation is doing nothing.