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.
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.
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)).
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;
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With