Liquid Studio Documentation
Tutorials / W3C XSD Schema Tutorial / XSD Tutorial - Part 4 - Namespaces
In This Topic
    XSD Tutorial - Part 4 - Namespaces
    In This Topic
    Namespaces

    So far we have glossed over namespaces entirely, we will hopefully address this a little now. Firstly the full namespacing rules are rather complicated, so this will just be an overview. If your working with a schema that makes use of namespaces then XML Data Binding will save you a great deal of time as it takes this complexity away. If you're not using a data binding tool then you may want to refer to the XSD standard or purchase a book!

    Namespaces are a mechanism for breaking up your schemas. Up until now we have assumed that you only have a single schema file containing all your element definitions, but the XSD standard allows you to structure your XSD schemas by breaking them into multiple files. These child schemas can then be included into a parent schema.

    Breaking schemas into multiple files can have several advantages. You can create re-usable definitions that can used across several projects. They make definitions easier to read and version as they break down the schema into smaller units that are simpler to manage.

    In this example, the schema is broken out into 4 files.

    This all works fine without namespaces, but if different teams start working on different files, then you have the possibility of name clashes, and it would not always be obvious where a definition had come from. The solution is to place the definitions for each schema file within a distinct namespace.

    We can do this by adding the attribute targetNamespace into the schema element in the XSD file, i.e.:

    <?xml version="1.0"?>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
               targetNamespace="myNamespace">
       ...
    </xs:schema>

    The value of targetNamespace is just a unique identifier, typically companies use their URL followed by something to qualify it. In principle the namespace has no meaning, but some companies have used the URL where the schema is stored as the targetNamespace and so some XML parsers will use this as a hint path for the schema e.g.: targetNamespace="http://www.microsoft.com/CommonTypes.xsd", but the following would be just as valid targetNamespace="my-common-types".

    Placing the targetNamespace attribute at the top of your XSD schema means that all entities defined in it are part of this namespace. So in our example above each of the 4 schema files could have a distinct targetNamespace value.

    Let's look at them in detail.

    CommonTypes.xsd

    <?xml version="1.0" encoding="utf-16"?>
    <!-- Created with Liquid XML Studio (https://www.liquid-technologies.com) -->
    <xs:schema targetNamespace="http://NamespaceTest.com/CommonTypes" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
    <xs:complexType name="AddressType">
    <xs:sequence>
    <xs:element name="Line1" type="xs:string" />
    <xs:element name="Line2" type="xs:string" />
    </xs:sequence>
    </xs:complexType>
    <xs:simpleType name="PriceType">
    <xs:restriction base="xs:decimal">
    <xs:fractionDigits value="2" />
    </xs:restriction>
    </xs:simpleType>
    <xs:simpleType name="PaymentMethodType">
    <xs:restriction base="xs:string">
    <xs:enumeration value="VISA" />
    <xs:enumeration value="MasterCard" />
    <xs:enumeration value="Cash" />
    <xs:enumeration value="Amex" />
    </xs:restriction>
    </xs:simpleType>
    </xs:schema>

    This schema defines some basic re-usable entities and types.
    The use of the targetNamespace attribute in the <xs:schema> element ensures all the enclosed definitions (AddressType, PriceType and PaymentMethodType) are all in the namespace "http://NamespaceTest.com/CommonTypes".

    CustomerTypes.xsd

    <?xml version="1.0" encoding="utf-16"?>
    <!-- Created with Liquid XML Studio (https://www.liquid-technologies.com) -->
    <xs:schema     xmlns:cmn="http://NamespaceTest.com/CommonTypes" 
                   targetNamespace="http://NamespaceTest.com/CustomerTypes" 
                   xmlns:xs="http://www.w3.org/2001/XMLSchema"
                   elementFormDefault="qualified">
        <xs:import schemaLocation="CommonTypes.xsd" 
                   namespace="http://NamespaceTest.com/CommonTypes"/>
    <xs:complexType name="CustomerType">
    <xs:sequence>
    <xs:element name="Name" type="xs:string" />
    <xs:element name="DeliveryAddress" type="cmn:AddressType" />
    <xs:element name="BillingAddress" type="cmn:AddressType" />
    </xs:sequence>
    </xs:complexType>
    </xs:schema>

    This schema defines the entity CustomerType, which makes use of the AddressType defined in the CommonTypes.xsd schema. We need to do a few things in order to use this.
    First we need to import that schema into this one - so we can see it. This is done using <xs:import>.
    It is worth noting the presence of the targetNamespace attribute at this point. This means that all entities defined in this schema belong to the namespace "http://NamespaceTest.com/CustomerTypes".
    So in order to make use of the AddressType which is defined in CustomerTypes.xsd, and part of the namespace "http://NamespaceTest.com/CommonTypes", we must fully qualify it. In order to do this we must define an alias for the namespace "http://NamespaceTest.com/CommonTypes". Again this is done using <xs:schema>.
    The line xmlns:cmn="http://NamespaceTest.com/CommonTypes" specifies that the alias cmn represents the namespace "http://NamespaceTest.com/CommonTypes".
    We can now make use of the types within the CommonTypes.xsd schema. When we do this we must fully qualify them as they are not in the same targetNamespace as the schema that is using them. We do this as follows: type="cmn:AddressType".

    OrderType.xsd

    <?xml version="1.0" encoding="utf-16"?>
    <!-- Created with Liquid XML Studio (https://www.liquid-technologies.com) --> <xs:schema xmlns:cmn="http://NamespaceTest.com/CommonTypes" targetNamespace="http://NamespaceTest.com/OrderTypes" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> <xs:import namespace="http://NamespaceTest.com/CommonTypes" schemaLocation="CommonTypes.xsd" />
    <xs:complexType name="OrderType">
    <xs:sequence>
    <xs:element maxOccurs="unbounded" name="Item">
    <xs:complexType>
    <xs:sequence>
    <xs:element name="ProductName" type="xs:string" />
    <xs:element name="Quantity" type="xs:int" />
    <xs:element name="UnitPrice" type="cmn:PriceType" />
    </xs:sequence>
    </xs:complexType>
    </xs:element>
    </xs:sequence>
    </xs:complexType>
    </xs:schema>

    This schema defines the type OrderType which is within the namespace http://NamespaceTest.com/OrderTypes.
    The constructs used here are the same as those used in CustomerTypes.xsd.

    Main.xsd

    <?xml version="1.0" encoding="utf-16"?>
    <!-- Created with Liquid XML Studio (https://www.liquid-technologies.com) --> <xs:schema xmlns:ord="http://NamespaceTest.com/OrderTypes" xmlns:pur="http://NamespaceTest.com/Purchase" xmlns:cmn="http://NamespaceTest.com/CommonTypes" xmlns:cust="http://NamespaceTest.com/CustomerTypes" targetNamespace="http://NamespaceTest.com/Purchase" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> <xs:import schemaLocation="CommonTypes.xsd" namespace="http://NamespaceTest.com/CommonTypes" />
    <xs:import schemaLocation="CustomerTypes.xsd" namespace="http://NamespaceTest.com/CustomerTypes" />
    <xs:import schemaLocation="OrderTypes.xsd" namespace="http://NamespaceTest.com/OrderTypes" />
    <xs:element name="Purchase">
    <xs:complexType>
    <xs:sequence>
    <xs:element name="OrderDetail" type="ord:OrderType" />
    <xs:element name="PaymentMethod" type="cmn:PaymentMethodType" />
    <xs:element ref="pur:CustomerDetails"/>
    </xs:sequence>
    </xs:complexType>
    </xs:element>
    <xs:element name="CustomerDetails" type="cust:CustomerType"/>
    </xs:schema>

    The elements in this schema are part of the namespace "http://NamespaceTest.com/Purchase" (see tagetNamespace attribute).
    This is our main schema and defines the concrete elements "Purchase", and "CustomerDetails" .
    This element builds on the other schemas, so we need to import them all, and define aliases for each namespace.
    Note: The element "CustomerDetails" which is defined in main.xsd is referenced from within "Purchase".

    The XML

    Because the root element Purchase is in the namespace "http://NamespaceTest.com/Purchase", we must quantify the <Purchase> element within the resulting XML document. Let's look at an example:

    <?xml version="1.0"?>
    <!-- Created with Liquid XML Studio (https://www.liquid-technologies.com) -->
    <p:Purchase xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://NamespaceTest.com/Purchase Main.xsd"
    xmlns:p="http://NamespaceTest.com/Purchase"
    xmlns:o="http://NamespaceTest.com/OrderTypes"
    xmlns:c="http://NamespaceTest.com/CustomerTypes"
    xmlns:cmn="http://NamespaceTest.com/CommonTypes">
    <p:OrderDetail>
    <o:Item>
    <o:ProductName>Widget</o:ProductName>
    <o:Quantity>1</o:Quantity>
    <o:UnitPrice>3.42</o:UnitPrice>
    </o:Item>
    </p:OrderDetail>
    <p:PaymentMethod>VISA</p:PaymentMethod>
    <p:CustomerDetails>
    <c:Name>James</c:Name>
    <c:DeliveryAddress>
    <cmn:Line1>15 Some Road</cmn:Line1>
    <cmn:Line2>SomeTown</cmn:Line2>
    </c:DeliveryAddress>
    <c:BillingAddress>
    <cmn:Line1>15 Some Road</cmn:Line1>
    <cmn:Line2>SomeTown</cmn:Line2>
    </c:BillingAddress>
    </p:CustomerDetails>
    </p:Purchase>

    The first thing we see is the xsi:schemaLocation attribute in the root element. This tells the XML parser that elements within the namespace "http://NamespaceTest.com/Purchase" can be found in the file "Main.xsd" (Note the namespace and URL are separated with whitespace - carriage return or space will do).

    The next thing we do is define some aliases

    You have probably noticed that every element in the schema is qualified with one of these aliases.

    The general rules for this are:

    The alias must be the same as the target namespace in which the element is defined. It is important to note that this is where the element is defined - not where the complexType is defined.
    So the element <OrderDetail> is actually defined in main.xsd so it is part of the namespace "http://NamespaceTest.com/Purchase", even though it uses the complexType "OrderType" which is defined in the OrderTypes.xsd. The contents of <OrderDetail> are defined within the complexType "OrderType", which is in the target namespace "http://NamespaceTest.com/OrderTypes", so the child element <Item> needs qualifying within the namespace "http://NamespaceTest.com/OrderTypes".

    The Effect of elementFormDefault

    You may have noticed that each schema contained an attribute elementFormDefault="qualified". This has 2 possible values, qualified, and unqualified, the default is unqualified. This attribute changes the namespacing rules considerably. It is normally easier to set it to qualified.

    So to see the effects of this property, if we set it to be unqualified in all of our schemas, the resulting XML would look like this:

    <?xml version="1.0"?>
    <!-- Created with Liquid XML Studio (https://www.liquid-technologies.com) --> <p:Purchase xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://NamespaceTest.com/Purchase Main.xsd" xmlns:p="http://NamespaceTest.com/Purchase"> <OrderDetail> <Item> <ProductName>Widget</ProductName> <Quantity>1</Quantity> <UnitPrice>3.42</UnitPrice> </Item> </OrderDetail> <PaymentMethod>VISA</PaymentMethod> <p:CustomerDetails> <Name>James</Name> <DeliveryAddress> <Line1>15 Some Road</Line1> <Line2>SomeTown</Line2> </DeliveryAddress> <BillingAddress> <Line1>15 Some Road</Line1> <Line2>SomeTown</Line2> </BillingAddress> </p:CustomerDetails> </p:Purchase>

    This is considerably different from the previous XML document.

    These general rules now apply:

    The first element is Purchase, this is defined globally in the Main.xsd schema, and therefore needs qualifying within the schemas target namespace "http://NamespaceTest.com/Purchase".

    The first child element is <OrderDetail> and is defined inline in Main.xsd->Purchase. So it does not need to be aliased.

    The same is true for all the child elements, they are all defined inline, so they do not need qualifying with a namespace.

    The final child element <CustomerDetails> is a little different. As you can see we have defined this as a global element within the targetNamespace "http://NamespaceTest.com/Purchase". In the element "Purchase" we just reference it. Because we are using a reference to an element, we must take into account its namespace, thus we alias it <p:CustomerDetails>.

    Summary

    Namespaces provide a useful way of breaking schemas down into logical blocks, which can then be re-used throughout a company or project. The rules for namespacing in the resulting XML documents are rather complex, the rules provided here are a rough guide, things do get more complex as you dig further into it. For this reason tools to deal with these complexities are useful, see XML Data Binding.