Reputation: 9614
I'm not sure if the title of my question is correct, I don't think this is be called "embedded" document, but I dont know the exact term. So, first of all: "What does this guy mean?"
<somedocument>
<item cost="34">
<name>Apple</name>
<description>A delicious apple!</description>
<details xmlns:apple="http://example.org/apples">
<apple:color>red</apple:color>
<apple:cultivar>Braeburn</apple:cultivar>
</details>
</item>
</somedocument>
Now I want to validate this document using XMLSchema. I would create a schema for all the generic details of the document like the item structure and such. I know that I can use <xsd:any>
to allow any kind of other XML within another document, which I can use to allow the apple specific elements inside the <details>
section. So far, so good.
But what if I want to validate all the elements in the http://example.org/apples
namespace? Given that the document is not limited to apples, there might be bananas, kiwis and peaches and may grow in the future to have even more "embedded" documents.
I there something in XMLSchema I missed which can help me do that? If not, what do you do in such cases? What are alternatives you can think of? As I use the Schemas also for documentation a "just parse the document and validate all the <details>
yourself in code" solution isn't ideal.
Thank you in advance!
Edit: After reading my whole question again there is a big point missing:
<details>
section is not limited to key/value pairs as seen in the example, so replacing the whole namespace and custom element stuff with a generic key/value solution does not work.Upvotes: 1
Views: 1778
Reputation: 7126
You can't easily achieve the exact schema design that you specify (where the details element can have variable content). Consider how an XML validator works: it would need to look ahead at the contents of the <details>
element to determine the type of the content. There is a related discussion here.
However, you can achieve a similar effect in one of two ways. In your circumstance, where you control the entire schema, I suggest option 2, because it is simpler (option 1 is better when the extension types might be in an external document, such as a third-party schema).
When you use inheritance and polymorphism in your XSD, you define an abstract ComplexType in your XSD, then define one or more types that extend it. You specify the abstract type as a member of the item
element, but in the XML document you specify the concrete type by using the xsi:type
attribute. With this technique, your XML document would look like this:
<?xml version="1.0" encoding="utf-8" ?>
<somedocument
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://grocery.example.com/schema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<item cost="34">
<name>Apple</name>
<description>A delicious apple!</description>
<details xsi:type="AppleDetails">
<color>red</color>
<cultivar>Braeburn</cultivar>
</details>
</item>
<item cost="32">
<name>Baked Beans</name>
<description>A tin of beans.</description>
<details xsi:type="BeansDetails">
<manufacturer>Heinz</manufacturer>
<size>440g</size>
</details>
</item>
</somedocument>
Here's the schema for the above example:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema
attributeFormDefault="unqualified"
elementFormDefault="qualified"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://grocery.example.com/schema"
xmlns="http://grocery.example.com/schema">
<xs:complexType name="ItemDetails" abstract="true" />
<xs:complexType name="AppleDetails">
<xs:complexContent>
<xs:extension base="ItemDetails">
<xs:sequence>
<xs:element minOccurs="0" name="color" type="xs:string" />
<xs:element minOccurs="0" name="cultivar" type="xs:string" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="BeansDetails">
<xs:complexContent>
<xs:extension base="ItemDetails">
<xs:sequence>
<xs:element minOccurs="0" name="manufacturer" type="xs:string" />
<xs:element minOccurs="0" name="size" type="xs:string" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="somedocument">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="item">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="description" type="xs:string" />
<xs:element name="details" type="ItemDetails" />
</xs:sequence>
<xs:attribute name="cost" type="xs:unsignedByte" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
<xs:choice>
When you use the <xs:choice>
element, you specify a number of mutually exclusive elements that can be used within the <item>
element. Here's what the document might look like:
<?xml version="1.0" encoding="utf-8" ?>
<somedocument
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://grocery.example.com/schema/2">
<item cost="34">
<name>Apple</name>
<description>A delicious apple!</description>
<appleDetails>
<color>red</color>
<cultivar>Braeburn</cultivar>
</appleDetails>
</item>
<item cost="32">
<name>Baked Beans</name>
<description>A tin of beans.</description>
<beansDetails>
<manufacturer>Heinz</manufacturer>
<size>440g</size>
</beansDetails>
</item>
</somedocument>
The corresponding schema is much simpler than option 1, but requires all item details types to be predefined before they are used in the <xs:choice>
. The schema looks like this:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema
attributeFormDefault="unqualified"
elementFormDefault="qualified"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://grocery.example.com/schema/2"
xmlns="http://grocery.example.com/schema/2">
<xs:complexType name="AppleDetails">
<xs:sequence>
<xs:element minOccurs="0" name="color" type="xs:string" />
<xs:element minOccurs="0" name="cultivar" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="BeansDetails">
<xs:sequence>
<xs:element minOccurs="0" name="manufacturer" type="xs:string" />
<xs:element minOccurs="0" name="size" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:element name="somedocument">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="item">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="description" type="xs:string" />
<xs:choice>
<xs:element name="appleDetails" type="AppleDetails" />
<xs:element name="beansDetails" type="BeansDetails" />
</xs:choice>
</xs:sequence>
<xs:attribute name="cost" type="xs:unsignedByte" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Because you control the entire schema, and can keep everything inside a single XSD schema document, the better choice is probably option 2.
Also, consider how to version the schema (see here for some suggestions) so that you can extend the schema and add new item types without breaking backwards compatibility.
Upvotes: 2
Reputation: 2645
I hope I understand the question correctly... inside <details>
, you want to allow any element from any namespace, but if the elements are in http://example.org/apples
(SIR I HAVE TO ASK YOU, DO YOU OWN THE EXAMPLE.ORG DOMAIN? :D), then you want to validate those.
Easy enough.
In your main schema file someschema.xsd
, do something like:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:someschema"
elementFormDefault="qualified"
>
<xs:import namespace="http://example.org/apples" schemaLocation="apples.xsd" />
<!-- rest of your schema here -->
<xs:complexType name="details">
<xs:sequence maxOccurs="unbounded">
<xs:any minOccurs="0" processContents="lax" />
</xs:sequence>
</xs:complexType>
</xs:schema>
create an apples.xsd
:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://example.org/apples"
elementFormDefault="qualified"
>
<xs:element name="color">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="red" />
<xs:enumeration value="green" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="cultivar">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="Braeburn" />
<xs:enumeration value="Granny Smith" />
<xs:enumeration value="McIntosh" />
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:schema>
???
The important thing is that for those foreign namespaces you do want to validate inside <details>
, you need to import the corresponding schema file at the top of your schema.
This actually leverages the fact that XML Schema is utterly retarded and not even capable of defining what the root element of your document is. Theoretically, you could alternatively also omit the <import>
, and instead simply validate your document against both someschema.xsd
and apples.xsd
(haven't tested that though).
Upvotes: 1