Udostępnij za pośrednictwem


Refactoring XmlValidatingReader in .NET 2.0

I seem to have come across this several times in the past week or two, so it's worth making a note.  In .NET 2.0 XmlValidatingReader is obsolete.  You need to do something like the following in order to create a validating reader now:

 XmlReaderSettings settings = new XmlReaderSettings();

settings.ValidationType = ValidationType.Schema;
                
XmlReader reader = XmlReader.Create(new XmlTextReader(pathToYourXmlFile), settings);

settings.Schemas.Add(null, 
  new XmlTextReader(Assembly.GetExecutingAssembly().GetManifestResourceStream("NameOfYourSchema.xsd")));

// Do your XML thing here...
                
reader.Close();

All the interesting configuration for the validation now hangs off the XmlReaderSettings.  Stuff like the ValidationEventHandler event. 

By the way, don't forget that XmlReader supports line and column information via the IXmlLineInfo interface.  If you catch an XmlSchemaException you first use the C# as operator to cast the reader to an IXmlLineInfo (just to be safe - in an exception handler you might not be sure where the reader came from), check for null, and then if you have an interface call HasLineInfo to see if you got far enough along in the reading to have any line information.

One thing that I tend to do with validating readers is to process the XML in two phases, like some compilers. In the first phase I focus on validation, and spit out as many errors as possible by catching the validation event.  Then, if all went well you can re-read the XML with a non-validating reader and you only have to focus on the remaining symantic errors that are not or cannot be caught by your schema.   Obviously, not a good idea if you are reading gobs and gobs of XML.  Your call. 

Lastly, don't forget to "manually" check your XML's default namespace is correctly referring to one of your validation schemas, because if its not your validating reader won't load the correct schema from your schema set and therefore won't give you any errors.   Something like this:

 while (!validatingReader.EOF)
{
  validatingReader.Read();
  
  if (validatingReader.NodeType == XmlNodeType.Element &&
    validatingReader.Name == "RootElement")
  {
    string defaultNamespace = validatingReader.LookupNamespace("");
    bool found = false;
     
    foreach (string namespaceUri in validNamespaceUris)
      if (defaultNamespace == namespaceUri)
      {
        found = true;
        break;
      }
              
    if (!found)
    {
      IXmlLineInfo lineInfo = (IXmlLineInfo)validatingReader.Reader;
      
      throw new XmlSchemaException(String.Format("{0}({1},{2}): error: The default namespace '{3}' is invalid", 
        validatingReader.BaseURI, lineInfo.LineNumber, lineInfo.LinePosition, defaultNamespace), null);
    }
  
    // Now validate the rest of the file, without the check
    while (validatingReader.Read());
  }
}

Comments