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: