tags:

views:

1475

answers:

2

I try to define a schema for XML documents I receive.

The documents look like:

<root>
    <items>
        <group name="G-1">
            <item name="I-1"/>
            <item name="I-2"/>
            <item name="I-3"/>
            <item name="I-4"/>
        </group>
    </items>
    <data>
        <group name="G-1" place="here">
            <customer name="C-1">
                <item name="I-1" count="3"/>
                <item name="I-2" count="4"/>
            </customer>
            <customer name="C-2">
                <item name="I-3" count="7"/>
            </customer>
        </group>
    </data>
</root>

I tried XmlSpy and xsd.exe from .NET 2.0. Both created schema definitions which allow below <group> any number of <item> and <customer> elements. But what I'm looking for should restrict <group> below <items> to <item> elements, and <group> below <data> to <customer> elements.

Is this something xml schema is not capable at all?

+1  A: 

Yes, XSD can handle this. I generated this schema from Visual Studio 2008 (much faster than doing it by hand) and it will do what you're looking for:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"&gt;
  <xs:element name="root">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="items">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="group">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element maxOccurs="unbounded" name="item">
                      <xs:complexType>
                        <xs:attribute name="name" type="xs:string" use="required" />
                      </xs:complexType>
                    </xs:element>
                  </xs:sequence>
                  <xs:attribute name="name" type="xs:string" use="required" />
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:element name="data">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="group">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element maxOccurs="unbounded" name="customer">
                      <xs:complexType>
                        <xs:sequence>
                          <xs:element maxOccurs="unbounded" name="item">
                            <xs:complexType>
                              <xs:attribute name="name" type="xs:string" use="required" />
                              <xs:attribute name="count" type="xs:unsignedByte" use="required" />
                            </xs:complexType>
                          </xs:element>
                        </xs:sequence>
                        <xs:attribute name="name" type="xs:string" use="optional" />
                      </xs:complexType>
                    </xs:element>
                  </xs:sequence>
                  <xs:attribute name="name" type="xs:string" use="required" />
                  <xs:attribute name="place" type="xs:string" use="required" />
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>
Peter Meyer
No problem! Glad to help.
Peter Meyer
+1  A: 

See XML Schema Runtime Polymorphism via xsi:type and Abstract Types

The key points are: (see the link for complete and correct context/placement/usage)
1) Create a base type with (abstract="true" to prevent it from being used directly)
Note: the ref attribute replaces the name attribute for elements defined elsewhere


<xs:complexType name="CustomerType" abstract="true" >
  <xs:sequence>
    <xs:element ref="cust:FirstName" />
    <xs:element ref="cust:LastName" />
    <xs:element ref="cust:PhoneNumber" minOccurs="0"/>
  </xs:sequence>
  <xs:attribute name="customerID" type="xs:integer" />
</xs:complexType>

2) Create two or more derived types by extending or restricting the base type


<xs:complexType name="MandatoryPhoneCustomerType" >
  <xs:complexContent>
    <xs:restriction base="cust:CustomerType">
      <xs:sequence>
        <xs:element ref="cust:FirstName" />
        <xs:element ref="cust:LastName" />
        <xs:element ref="cust:PhoneNumber" minOccurs="1" />
      </xs:sequence>

    </xs:restriction>
  </xs:complexContent>
</xs:complexType>

and


<xs:complexType name="AddressableCustomerType" >
  <xs:complexContent>
    <xs:extension base="cust:CustomerType">
      <xs:sequence>
        <xs:element ref="cust:Address" />
        <xs:element ref="cust:City" />
        <xs:element ref="cust:State" />

        <xs:element ref="cust:Zip" />

      </xs:sequence>

    </xs:extension>
  </xs:complexContent>
</xs:complexType>

3) Reference the base type in an element


<xs:element name="Customer" type="cust:CustomerType" />

4) In your instance XML document, specify the specific derived type as an xsi:type attribute


<cust:Customer customerID="12345" xsi:type="cust:MandatoryPhoneCustomerType" >
  <cust:FirstName>Dare</cust:FirstName>
  <cust:LastName>Obasanjo</cust:LastName>
  <cust:PhoneNumber>425-555-1234</cust:PhoneNumber>
</cust:Customer>

or

<cust:Customer customerID="67890" xsi:type="cust:AddressableCustomerType" >
  <cust:FirstName>John</cust:FirstName>
  <cust:LastName>Smith</cust:LastName>
  <cust:Address>2001</cust:Address>
  <cust:City>Redmond</cust:City>
  <cust:State>WA</cust:State>
  <cust:Zip>98052</cust:Zip>
</cust:Customer>

6eorge Jetson