Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I resolve the schemaLocation attribute of an .XSD when all of my .XSD's are stored as resources?

Tags:

c#

xml

.net-4.0

xsd

I am working on a project where I need to generate XML files based on nested XSD's. e.g. ORDER has a reference to PERSON, PERSON has a reference to ADDRESS, etc.

I am creating an `XmlReaderSettings' instance to validate the XSD's, as well as validate the XML after it is generated.

I have added the XSD's as Resources to my assembly. I then create an XmlSchema instance for each resource, from lowest to highest, and add it to the XmlReaderSettings.Schemas collection.

However, this fails attempting to add a schema that references another schema. I get an XmlSchemaException: "For element declaration, either the name or the ref attribute must be present."

I have included sample XSD's and source code below:

ADDRESS.xsd - referenced by PERSON.xsd

<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="ADDRESS.xsd" attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="ADDRESS" >
    <xs:complexType>
      <xs:sequence>
        <xs:element name="ADDRESS1" type="xs:string"/>
        <xs:element name="ADDRESS2" type="xs:string"/>
        <xs:element name="CITY" type="xs:string"/>
        <xs:element name="STATE" type="xs:string"/>
        <xs:element name="ZIP" type="xs:string"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

PERSON.xsd

<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="PERSON.xsd" attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:include schemaLocation="ADDRESS.xsd"/>
  <xs:element name="PERSON" >
    <xs:complexType>
      <xs:sequence>
        <xs:element name="L_NAME" type="xs:string"/>
        <xs:element name="F_NAME" type="xs:string"/>
        <xs:element name="Addresses">
          <xs:complexType>
            <xs:sequence>
              <xs:element ref="ADDRESS" minOccurs="0" maxOccurs="unbounded"/>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Sample Code - Load and validate XSD's

using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using SchemaTest.Properties;

namespace SchemaTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // create validation settings instance
            var xmlReaderSettings = new XmlReaderSettings
            {
                ValidationType = ValidationType.Schema,
                ValidationFlags = XmlSchemaValidationFlags.ProcessInlineSchema |
                                  XmlSchemaValidationFlags.ProcessSchemaLocation |
                                  XmlSchemaValidationFlags.ReportValidationWarnings
            };

            xmlReaderSettings.ValidationEventHandler += SchemaValidationEventHandler;

            // added XmlResourceResolver, per the accepted answer below
            xMLReaderSettings.Schemas.XmlResolver = new XmlResourceResolver();

            // load schemas into validation settings instance
            LoadSchema(xmlReaderSettings, Resources.PERSON);

            // validate schemas
            xmlReaderSettings.Schemas.Compile();

            Console.WriteLine("Press any key to continue...");
            Console.ReadKey();
        }

        private static void LoadSchema(XmlReaderSettings xmlReaderSettings, string schemaString)
        {
            using (var schemaStream = new MemoryStream(Encoding.Default.GetBytes(schemaString)))
            {
                var schema = XmlSchema.Read(schemaStream, null);

                try
                {
                    xmlReaderSettings.Schemas.Add(schema);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("EXCEPTION: {0}", ex.Message);
                }
            }
        }

        private static void SchemaValidationEventHandler(object sender, ValidationEventArgs e)
        {
            Console.WriteLine("{0}: {1}", e.Severity, e.Message);
        }
    }
}

I am not sure if this has anything to do with the ValidationFlags, but I tried removing XmlSchemaValidationFlags.ProcessSchemaLocation and still received the same error. I have also tried passing SchemaValidationEventHandler to the XmlSchema.Read method (instead of null in sample code), and the schema seems to be created OK, but it still throws the exception when attemtping to add it to the XmlReaderSettings.Schemas collection.

EDIT - 2011.11.03

I resolved this issue (pun intended) by creating my own XmlUrlResolver descendent called XmlResourceResolver, based on the XmlResolver class example in accepted answer below.

like image 269
Welton v3.61 Avatar asked Nov 02 '11 14:11

Welton v3.61


People also ask

What is schemaLocation in XSD?

The xsi:schemaLocation attribute locates schemas for elements and attributes that are in a specified namespace. Its value is a namespace URI followed by a relative or absolute URL where the schema for that namespace can be found. It is most commonly attached to the root element but can appear further down the tree.

What is attributeFormDefault in XSD?

The xsd setting, attributeFormDefault="qualified", indicates that child attributes should be emitted with the namespaces of the defining schema. (The default attributeFormDefault="unqualified", indicates that child attributes should be emitted as unqualified (no namespace)).


1 Answers

To get rid of the error, change type="Addresses" to name="Addresses" in your PERSON.xsd file.

It is better to go with the code below to get schemas compiled. The important things are to make sure you establish a base uri (since you're reading from a stream), to use an XmlSchemaSet instead, and a custom resolver (to read files as embedded resources).

using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Schema;

namespace ConsoleApplication3
{
    class Program
    {
        class XmlResolver : XmlUrlResolver
        {
            internal const string BaseUri = "schema://";

            public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
            {
                if (absoluteUri.Scheme == "schema")
                {
                    switch (absoluteUri.LocalPath)
                    {
                        case "/ADDRESS.xsd":
                            return new MemoryStream(Encoding.UTF8.GetBytes(Resource.ADDRESS));
                        case "/PERSON.xsd":
                            return new MemoryStream(Encoding.UTF8.GetBytes(Resource.PERSON));
                    }
                }
                return base.GetEntity(absoluteUri, role, ofObjectToReturn);
            }
        }

        static void Main(string[] args)
        {
            using (XmlReader reader = XmlReader.Create(new StringReader(Resource.PERSON), new XmlReaderSettings(), XmlResolver.BaseUri))
            {
                XmlSchemaSet xset = RetrieveSchemaSet(reader);
                Console.WriteLine(xset.IsCompiled);
            }
        }

        private static XmlSchemaSet RetrieveSchemaSet(XmlReader reader)
        {
            XmlSchemaSet xset = new XmlSchemaSet() { XmlResolver = new XmlResolver() };
            xset.Add(XmlSchema.Read(reader, null));
            xset.Compile();
            return xset;
        }
    }
}
like image 116
Petru Gardea Avatar answered Oct 12 '22 01:10

Petru Gardea