Wednesday, October 24, 2012

XML Schema Design Patterns

There are a handful of ways to design XML schemas. Here's a brief overview of each type, starting with the simplest and most widely recognized (Russian Doll) and concluding with my preferred design pattern (Garden of Eden). I like the Garden of Eden design pattern because it offers maximum reuse opportunity.

We'll use the following XML file as our source file. Each of the design patterns described below are various ways to define the schema for this XML file.

<?xml version="1.0" encoding="UTF-8"?>
<Customer>
  <CustomerId>10</CustomerId>
  <FirstName>John</FirstName>
  <LastName>Doe</LastName>
  <Address>
    <StreetAddress>101 First Street</StreetAddress>
    <City>Somewhere</City>
    <State>ND</State>
    <Zip>12345</Zip>
  </Address>
</Customer>


Russian Doll


The Russian Doll design pattern is usually what you get when you auto-generate an XSD from an XML source file. It simply nests all of the XML elements, very much resembling the source XML file itself. It's simple and easy to create, but if you reference it from other XSDs or WSDLs, you lose the ability to reuse types within the file. For example, if we wanted to re-use the Address type, we can't because it's not externalized to be referenced anywhere else. It's nested within the Customer type and can't be referenced by any other XSD or WSDL. Here's the Russian Doll example.

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://www.example.org/Customer"
        xmlns:tns="http://www.example.org/Customer"
        elementFormDefault="qualified">

    <xsd:element name="Customer">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element name="CustomerId" type="xsd:int" />
                <xsd:element name="FirstName" type="xsd:string" />
                <xsd:element name="LastName" type="xsd:string" />
                <xsd:element name="Address">
                    <xsd:complexType>
                        <xsd:sequence>
                            <xsd:element name="StreetAddress" type="xsd:string"/>
                            <xsd:element name="City" type="xsd:string"/>
                            <xsd:element name="State" type="xsd:string"/>
                            <xsd:element name="Zip" type="xsd:string"/>
                        </xsd:sequence>
                    </xsd:complexType>
                </xsd:element>
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>
   
</xsd:schema>


Salami Slice

The Salami Slice design pattern moves all of the primitive type elements into global definitions. So, we globally define everything except Customer and Address. What this gains us is reuse of the primitive fields CustomerId, FirstName, LastName, StreetAddress, City, State, and Zip. This is a big gain. We can reuse these types within other types in the same XSD (which is commonly the reason for using Salami Slice) or we can even reuse them from other XSDs and WSDLs. Here's an example of our Customer XML file defined with an XSD designed using the Salami Slice design pattern.

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://www.example.org/Customer"
        xmlns:tns="http://www.example.org/Customer"
        elementFormDefault="qualified">

    <xsd:element name="CustomerId" type="xsd:int" />
    <xsd:element name="FirstName" type="xsd:string" />
    <xsd:element name="LastName" type="xsd:string" />
    <xsd:element name="StreetAddress" type="xsd:string"/>
    <xsd:element name="City" type="xsd:string"/>
    <xsd:element name="State" type="xsd:string"/>
    <xsd:element name="Zip" type="xsd:string"/>

    <xsd:element name="Customer">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element ref="tns:CustomerId" />
                <xsd:element ref="tns:FirstName" />
                <xsd:element ref="tns:LastName" />
                <xsd:element name="Address">
                    <xsd:complexType>
                        <xsd:sequence>
                            <xsd:element ref="tns:StreetAddress" />
                            <xsd:element ref="tns:City" />
                            <xsd:element ref="tns:State" />
                            <xsd:element ref="tns:Zip" />
                        </xsd:sequence>
                    </xsd:complexType>
                </xsd:element>
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>
   
</xsd:schema>



Venetian Blind

With the Venetian Blind design pattern, instead of making our primitive types global, we make our complex types global. This allows us to reuse data entities, while encapsulating the internals of the data entities. Here's an example.

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://www.example.org/Customer"
        xmlns:tns="http://www.example.org/Customer"
        elementFormDefault="qualified">

    <xsd:complexType name="Address.Type">
        <xsd:sequence>
            <xsd:element name="StreetAddress" type="xsd:string"/>
            <xsd:element name="City" type="xsd:string"/>
            <xsd:element name="State" type="xsd:string"/>
            <xsd:element name="Zip" type="xsd:string"/>
        </xsd:sequence>
    </xsd:complexType>
   
    <xsd:complexType name="Customer.Type">
        <xsd:sequence>
            <xsd:element name="CustomerId" type="xsd:int" />
            <xsd:element name="FirstName" type="xsd:string" />
            <xsd:element name="LastName" type="xsd:string" />
            <xsd:element name="Address" type="tns:Address.Type" />
        </xsd:sequence>
    </xsd:complexType>
   
    <xsd:element name="Customer" type="tns:Customer.Type" />

</xsd:schema>



Garden of Eden

The Garden of Eden approach strives for maximum reuse opportunity by making every element and complex type global. Personally, I like this design pattern the best. It gives you the ability to reference any type within the same XSD or from any other XSD or WSDL. Here's an example.

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://www.example.org/Customer"
        xmlns:tns="http://www.example.org/Customer"
        elementFormDefault="qualified">
   
    <xsd:element name="CustomerId" type="xsd:int"/>
    <xsd:element name="FirstName" type="xsd:string"/>
    <xsd:element name="LastName" type="xsd:string"/>
    <xsd:element name="StreetAddress" type="xsd:string"/>
    <xsd:element name="City" type="xsd:string"/>
    <xsd:element name="State" type="xsd:string"/>
    <xsd:element name="Zip" type="xsd:string"/>

    <xsd:element name="Address" type="tns:Address.Type"/>
    <xsd:element name="Customer" type="tns:Customer.Type"/>

    <xsd:complexType name="Address.Type">
        <xsd:sequence>
            <xsd:element ref="tns:StreetAddress"/>
            <xsd:element ref="tns:City"/>
            <xsd:element ref="tns:State"/>
            <xsd:element ref="tns:Zip"/>
        </xsd:sequence>
    </xsd:complexType>
     
    <xsd:complexType name="Customer.Type">
         <xsd:sequence>
             <xsd:element ref="tns:CustomerId"/>
             <xsd:element ref="tns:FirstName"/>
             <xsd:element ref="tns:LastName"/>
             <xsd:element ref="tns:Address"/>
         </xsd:sequence>
    </xsd:complexType>

</xsd:schema>

No comments:

Post a Comment