Geeks With Blogs
Willem's... {rue if I mellow}

As an ongoing part of maintaining our CodeXS code-generation tool (read about it here: CodeXS article), we are continually looking for ways to improve the consistency, reliability and performance of the CodeXS tool.

Enter the System.Xml.Schema.XmlSchemaSet class. Unfortunately, it is available only in .NET 2.0+. Unlike the .NET 1.x XmlSchemas class, this class provides what seems to be a one-shop-stop means of reading, compiling and using a target-schema that utilizes a complex multi-namespace, multi-include/import schema set. Merely by setting up the correct URL resolver, the target-schema can simply be Add'ed to the XmlSchemaSet and all the import'ed/include'ed schemas are automatically added during the execution of that method. The XmlSchemaSet object can then be Compile'd, and the various properties of the class can be used to extract the complex types, elements etc. of the entire schema set as if it is one large XmlSchema.

One caveat though: it seems as if there are schema sets that have bi-directional schema import constructs. In these cases the same schema may be added more than once to the schema set, resulting in validation errors when the XmlSchemaSet object is Compile'd. These typically result in a 'The {object}{namespace} has already been declared.' error. An example of this schema can be found here: Global Justice XML Version 3.0.3 Schema

One way around this problem is shown in the code-excerpt below, and currently works correctly for all the schemas we are using.

Read and compile a complex XML schema set
// Set the current working directory for file-based Uri's.
FileInfo fileInfo = new FileInfo(schemaFilePath);
Directory.SetCurrentDirectory(fileInfo.DirectoryName);
XmlSchemaSet xmlSchemaSet;
// Open the target schema file as a stream.
using (StreamReader reader = new StreamReader(schemaFilePath))
{
  // XmlSchemaSet is only .NET 2.0+.
  xmlSchemaSet = new XmlSchemaSet();
  xmlSchemaSet.ValidationEventHandler += ValidationErrorHandler;
  xmlSchemaSet.XmlResolver = new XmlUrlResolver();
  XmlSchema xmlSchema = XmlSchema.Read(reader, ValidationErrorHandler);
  // Adding a target schema here will automatically resolve all
  // include/import'ed schemas - these will reside together with the
  // target schema in the XmlSchemaSet.Schemas() collection of 
  // XmlSchema objects.
  xmlSchemaSet.Add(xmlSchema);
  // Check for uniqueness - schemas may be added twice if there
  // are bi-directional imports.
  // Using an ArrayList lets use indexing for accessing the schemas.
  ArrayList schemaList = new ArrayList(xmlSchemaSet.Schemas());
  // ArrayList holding the duplicate schemas.
  ArrayList duplicateSchemaList = new ArrayList();
  for (int i = 0; i < schemaList.Count; i++)
  {
    // It seems that if the SourceUri property is unresolved 
    // (=string.Empty), then the schema has already been added.
    if (((XmlSchema)schemaList[i]).SourceUri == string.Empty)
    {
      duplicateSchemaList.Add((XmlSchema)schemaList[i]);
    }
    else
    {
      // Exhaustively test for duplicate schemas by means of the
      // SourceUri property.
      for (int j = i + 1; j < schemaList.Count; j++)
      {
        if (((XmlSchema)schemaList[i]).SourceUri ==
            ((XmlSchema)schemaList[j]).SourceUri)
        {
          duplicateSchemaList.Add((XmlSchema)schemaList[j]);
        }
      }
    }
  }
  // Remove the duplicate schemas from the set.
  foreach (XmlSchema schema in duplicateSchemaList)
  {
    xmlSchemaSet.Remove(schema);
  }
  // Compile the whole schema set.
  xmlSchemaSet.Compile();
  reader.Close();
}

The CodeXS tool is available here:

You can download the source-code by re/registering here:

You can check the latest update information here:

You can run the CodeXS online tool here:

Posted on Thursday, September 14, 2006 7:42 AM .NET Adventures | Back to top


Comments on this post: Playing with the .NET 2.0 XmlSchemaSet class

# re: Playing with the .NET 2.0 XmlSchemaSet class
Requesting Gravatar...
Found another work-around here:

http://blogs.infosupport.com/wouterv/archive/2006/10/08/Validating-Open-XML-_2D00_-Getting-rid-of-circular-imports-in-the-XmlSchemaSet.aspx

This solution subclasses the XmlResolver class to do more-or-less the same thing, but it looks a lot cleaner. It may have a problem with include'd schemas from the same namespace, although this has not been confirmed.
Left by Willem on Oct 24, 2006 3:11 PM

# re: Playing with the .NET 2.0 XmlSchemaSet class
Requesting Gravatar...
Hi!
Does this really work for you? When I try adding a schema that has circular references (imports), the Schemas() method does only return empty list...
Left by TTom on Mar 09, 2007 4:12 PM

# re: Playing with the .NET 2.0 XmlSchemaSet class
Requesting Gravatar...
An approach that seems to be working is to load the schemas into a SchemaSet, and ignore any errors, then load a second schema set from the first, using the overload
XmlSchemaSet.Add(XmlSchemaSet schemas);

Something like this

System.Xml.Schema.XmlSchemaSet ssFirstPass = new System.Xml.Schema.XmlSchemaSet();
ssFirstPass.XmlResolver = new XmlUrlResolver();
ssFirstPass.ValidationEventHandler += new System.Xml.Schema.ValidationEventHandler(DUMMY_HANDLER);
ssFirstPass.Add("MySchema.xsd");


System.Xml.Schema.XmlSchemaSet ssSecondPass = new System.Xml.Schema.XmlSchemaSet();
ssSecondPass.XmlResolver = new XmlUrlResolver();
ssSecondPass.ValidationEventHandler += new System.Xml.Schema.ValidationEventHandler(schemaCollection_ValidationEventHandler);
ssSecondPass.Add(ssFirstPass);
ssSecondPass.Compile();
Left by Simon Sprott on May 19, 2008 3:53 PM

Your comment:
 (will show your gravatar)


Copyright © Willem Fourie | Powered by: GeeksWithBlogs.net