Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
261 views
in Technique[技术] by (71.8m points)

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

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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;
        }
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...