Extend complex types from an existing schema
I need to extend an existing XML document with new elements without losing the ability to val开发者_StackOverflow社区idate it against both the unchanged original schema and an extended schema defined by me.
For example, lets say I want to add Code
and Price
elements to an existing Book
document, as follows:
<aa:Book xmlns:aa="http://www.aa.com"
xmlns:bb="http://www.bb.com">
<aa:Title>Complete Works</aa:Title>
<aa:Author>Shakespeare</aa:Author>
<bb:Code>98</bb:Code>
<bb:Price>31.6</bb:Price>
</aa:Book>
The original schema (which I cannot change) would look something like this:
<xs:schema targetNamespace="http://www.aa.com"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.aa.com"
elementFormDefault="qualified">
<xs:element name="Book" type="Book--type" />
<xs:complexType name="Book--type">
<xs:sequence>
<xs:element name="Title" type="xs:string" />
<xs:element name="Author" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:schema>
How would I create a new schema that defines these new elements such that a SchemaSet
containing both these schemas can successfully validate the extended XML document above?
I've tried the following usual complex type extension pattern:
<xs:schema targetNamespace="http://www.bb.com"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.bb.com"
xmlns:aa="http://www.aa.com"
elementFormDefault="qualified">
<xs:import namespace="http://www.aa.com" />
<xs:complexType name="Book--type">
<xs:complexContent>
<xs:extension base="aa:Book--type">
<xs:sequence>
<xs:element name="Code" type="xs:int" />
<xs:element name="Price" type="xs:double" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
However this causes errors stating that the element aa:Book
doesn't define a child element bb:Code
, which makes total sense.
Is this even possible?
For reference, here is the code I'm using to validate the document:
var fail = new ValidationEventHandler((sender, e) => Debug.Fail(e.Message));
var schemaSet = new XmlSchemaSet();
schemaSet.Add(XmlSchema.Read(new StringReader(original), fail));
schemaSet.Add(XmlSchema.Read(new StringReader(extension), fail));
var settings = new XmlReaderSettings
{
ValidationType = ValidationType.Schema,
Schemas = schemaSet
};
settings.ValidationEventHandler += fail;
using (var stream = File.OpenRead(fileName))
using (var reader = XmlReader.Create(stream, settings))
{
while (reader.Read()) {}
}
It turns out that xs:redefine
is what I was looking for. It allows you to redefine an existing element in a new schema without touching the original schema or the document being validated.
Using my original example, a book can be extended using the following two schemas:
<xs:schema targetNamespace="http://www.bb.com"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.bb.com"
elementFormDefault="qualified">
<xs:element name="Code" type="xs:int" />
<xs:element name="Price" type="xs:double" />
</xs:schema>
<xs:schema targetNamespace="http://www.aa.com"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.aa.com"
xmlns:bb="http://www.bb.com"
elementFormDefault="qualified">
<xs:import namespace="http://www.bb.com" />
<xs:redefine schemaLocation="http://www.aa.com">
<xs:complexType name="Book--type">
<xs:complexContent>
<xs:extension base="Book--type">
<xs:sequence>
<xs:element ref="bb:Code" />
<xs:element ref="bb:Price" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:redefine>
</xs:schema>
The problem is that even though you have a new type defined in your schema B, the root element in your sample document is of type Book--type
from the original schema A.
You need to use a type override in your document, like so:
<aa:Book xmlns:aa="http://www.aa.com"
xmlns:bb="http://www.bb.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="bb:Book--type">
<aa:Title>Complete Works</aa:Title>
<aa:Author>Shakespeare</aa:Author>
<bb:Code>98</bb:Code>
<bb:Price>31.6</bb:Price>
</aa:Book>
精彩评论