My Technical Notes

Wednesday, 21 January 2015

XML Schemas: targetNamespace vs default namespace (xmlns)

Below is a typical `<xs:schema>` element found in an xsd file:


<xs:schema
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns="http://tahirhassan.blogspot.comn"
  targetNamespace="http://tahirhassan.blogspot.com"
  elementFormDefault="qualified"
  >
  ...
</xs:schema>

In the above snippet, any type defined in the document, (without a prefix, of course), will reside in the target namespace `http://tahirhassan.blogspot.com`. This is akin to defining a type in C# within a namespace:


namespace TahirHassanBlogspotCom
{
    public class Move
    {
        public int Loop { get; set; }
    }
}

In the above snippet, the `Move` type is in the namespace `TahirHassanBlogspotCom`. Here is the equivalent XML Schema definition file:


<xs:schema
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  targetNamespace="http://tahirhassan.blogspot.com"
  elementFormDefault="qualified"
  >
  <xs:complexType name="move">
    <xs:attribute name="loop" type="xs:int" />
  </xs:complexType>
</xs:schema>

In the above xsd, we have defined a type `move` in the namespace `http://tahirhassan.blogspot.com`, using the `targetNamespace` attribute. Any XML document which instantiates the `move` type must also "import" the namespace it is in, `http://tahirhassan.blogspot.com`:


<move 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xmlns="http://tahirhassan.blogspot.com" 
   xsi:type="move"
   loop="3" />

If an element has `xmlns="some-ns"` specified, then that element (and all its descending elements, unless specified otherwise) must belong in the namespace `some-ns` In the above example, we specify the namespace to be `http://tahirhassan.blogspot.com`, and the type to be `move`. It's a bit like instantiating our C# class defined above:


var obj = new TahirHassanBlogspotCom.Move { Loop = 3 };

If instead we want to go down the prefixing route, we associate a prefix with a namespace (e.g. `thb` ); we then prefix all elements in the namespace with this prefix:


<move 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xmlns:thb="http://tahirhassan.blogspot.com" 
   xsi:type="thb:move"
   loop="3" />

There is a reason why that in most XSD schema files, the `xmlns` and `targetNamespace` are the same. It is because we are using types defined in the same document. For example, if I define a type `shoe` (for example):


<xs:schema
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns="urn:tahirhassan:shoeexample"
  targetNamespace="urn:tahirhassan:shoeexample"
  elementFormDefault="qualified"
  >
  <xs:complexType name="shoe">
    <xs:attribute name="size" type="xs:int" />
  </xs:complexType>
</xs:schema>

Then the type `shoe` is in the namespace (`targetNamespace` element) `urn:tahirhassan:shoeexample`. If we need to use this type elsewhere in the document:


<xs:schema
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns="urn:tahirhassan:shoeexample"
  targetNamespace="urn:tahirhassan:shoeexample"
  elementFormDefault="qualified"
  >
  <xs:complexType name="shoeCollection">
    <xs:sequence minOccurs="1" maxOccurs="unbounded">
      <xs:element name="shoe" type="shoe" />
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="shoe">
    <xs:attribute name="size" type="xs:int" />
  </xs:complexType>
</xs:schema>

Then if we did not include the `xmlns`, then the xml parser will be looking for the type `shoe` in the default namespace (and it will not be found). However, since we have specified that the `xmlns` is `urn:tahirhassan:shoeexample`, it will first look for `shoe` in `urn:tahirhassan:shoeexample` before looking anywhere else.

To conclude, all (non-prefixed) types defined in a document will be in the namespace declared in `targetNamespace`. The reason why we also include the `xmlns` attribute is to resolve types found in the document as being in the same namespace.

TODO: Write an article on what `elementFormDefault="qualified"` does.

No comments: