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).
Option 1: Use inheritance and polymorphism
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>
Option 2: Use <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.