Click here to Skip to main content
Click here to Skip to main content

XSD Tutorial - Part 4 of 5 - Namespaces

, 3 Jul 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
This article gives a basic overview of the building blocks underlying XML Schemas.

XSD Tutorial Parts

  1. Elements and Attributes
  2. Conventions and Recommendations
  3. Extending Existing Types
  4. Namespaces
  5. Other Useful bits...

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 you are 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 be 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.

  • CommonTypes - this could contain all your basic types, AddressType, PriceType, PaymentMethodType etc.
  • CustomerTypes - this could contain all your definitions for your customers.
  • OrderTypes - this could contain all your definitions for orders.
  • Main - this would pull all the sub schemas together into a single schema, and define your main element/s.

All this 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 0.9.8.0 (http://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>

The previous XSD shown graphically using Liquid XML Studio

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 in the namespace "http://NamespaceTest.com/CommonTypes".

CustomerTypes.xsd

<?xml version="1.0" encoding="utf-16"?>
<!-- Created with Liquid XML Studio 0.9.8.0 (http://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>

The previous XSD shown graphically using Liquid XML Studio

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 0.9.8.0 (http://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>

The previous XSD shown graphically using Liquid XML Studio

This schema defines the type OrderType which is within the namepace 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 0.9.8.0 (http://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 previous XSD shown graphically using Liquid XML Studio

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 namesapce.

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. Lets look at an example:

<?xml version="1.0"?>
<!-- Created with Liquid XML Studio 0.9.8.0 (http://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 the 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

  • "p" to mean the namespace "http://NamespaceTest.com/Purchase"
  • "c" to mean the namespace "http://NamespaceTest.com/CustomerTypes"
  • "o" to mean the namespace "http://NamespaceTest.com/OrderTypes"
  • "cmn" to mean the namespace "http://NamespaceTest.com/CommonTypes"

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 that 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 qualifiing 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 qualifed.

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 0.9.8.0 (http://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:

  • Only root elements defined within a schema need qualifying with a namespace.
  • All types that are defined inline do NOT need to be qualified.

The first element is Purchase, this is defined gloablly 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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Simon Sprott
Software Developer (Senior) Liquid Technologies
United Kingdom United Kingdom
No Biography provided

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web04 | 2.8.141015.1 | Last Updated 3 Jul 2014
Article Copyright 2007 by Simon Sprott
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid