views:

22

answers:

2

I need to extend an existing XML document with new elements without losing the ability to validate 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"&gt;
  <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()) {}
}
A: 

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>
xcut
Unfortunately I'm not allowed to change the document being validated either! Fortunately I've discovered xs:redefine which looks like exactly what I need.
Nathan Baulch
+1  A: 

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"&gt;
    <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>
Nathan Baulch