You use namespaces in both XML Schemas and instance documents to tackle issues such as defining XML Schemas in multiple files and properly referencing XML Schema constructs within your instance documents. An XML Schema–validating XML processor such as XMLSPY makes extensive use of namespaces to control document validation.
Your XML Schema is a custom vocabulary of XML components that belong to and are uniquely identified by a target namespace. An XML Schema can have only one target namespace. All the schema components that you define in the XML Schema belong to your chosen target namespace. It is the target namespace that enables an XML processor to distinguish between various schema vocabularies and that, in turn, controls how an instance document is validated. As the schema author, you have several options in specifying XML component definitions within an XML Schema. These definitions affect how the components (such as elements and attributes) are represented in an XML instance document. The following sections show several examples of namespace settings and their implications for authoring XML instance documents.
On the CD The sample XML Schemas in this chapter are based on the Purchase Order XML Schema that you developed in Chapter 4. The file is contained within the XMLSPY Handbook Project, which installs itself as the default project when you use the exclusive version of XMLSPY included on the CD-ROM that accompanies this book. Look for the Order_5-01.xsd file in the ch5 folder. You can also find all subsequent example files referenced in this chapter in the ch5 folder.
If you are not using the version of XMLSPY from the CD, you can still access the example files. Simply locate the XMLSPYHandbook.spp file on the CD and copy it, along with all subdirectories, over to your local file system. Then open the file by choosing Project ? Open Project.
Throughout the chapter, I make various modifications to the Order_5-01. xsd file to illustrate a particular XML Schema concept. In general, each individual example uses the schema of Order_5-01.xsd as the starting point and then adds some additional concepts. This straightforward approach enables you to look at any one topic independently of the rest of the chapter. That should help you avoid confusing the many different schema concepts examined in this chapter. I suggest that you always keep an unmodified copy of the Order_5-01.xsd file somewhere on your local file system so that you can follow along with all the examples in this chapter.
Elements and attributes must be qualified, which means that you must somehow inform the XML processor what namespace they belong to, both in the XML Schema in which they are defined, and in any instance document that is meant to conform with a particular XML Schema. You can qualify an element or attribute in one of two ways: explicitly or implicitly. Explicit qualification of an element or attribute requires that you include a namespace prefix, followed by a colon and then the element or attribute name (<xsd:complexElement>). Implicit qualification of an element or attribute happens when you map a namespace to the default namespace, and then, any unqualified element or attribute appearing within the document is implied to belong to the default namespace.
To clarify the difference between explicit and implicit qualification of elements and attributes, consider the Purchase Order XML Schema example, provided in Order_5-01.xsd, which uses elements and attributes from two different namespaces. The assignment of namespaces to prefixes has been specified through the use of xmlns attributes placed in the root element of the XML Schema (that is, in the schema element) as shown below:
<xsd:schema targetNamespace=”http://www.company.com/examples/ purchaseorder” xmlns:xsd=”http://www.w3.org/2001/XMLSchema” xmlns= “http://www.company.com/examples/purchaseorder” elementFormDefault= “qualified” attributeFormDefault=”unqualified”>
For now, don’t worry about the two attributes included (and set equal to qualified and unqualified) in the preceding code. I included elementFormDefault and attributeFormDefault for completeness here; I discuss them in greater detail at the end of this section. For now, I want to focus on the namespace declarations.
The http://www.w3c.org/2001/XMLSchema namespace is mapped to the xsd prefix, and the http://www.company.com/examples/purchaseorder namespace is both specified to be the schema’s target namespace and is mapped to the document’s default namespace. This namespace and namespace prefix assignment configuration imply that any XML constructs (such as elements or attributes) whose definitions are found within the Purchase Order XML Schema file belong to the http://www.company.com/examples/purchaseorder namespace. Any XML Schema constructs defined in the http://www.w3c.org/2001/XMLSchema namespace, such as complexElement or sequence, must be explicitly qualified using the xsd prefix.
There is nothing special about this namespace configuration. In fact, I could just as easily have chosen to assign the http://www.w3c.org/2001/XMLSchema namespace to be the default namespace and required explicit qualification of XML constructs belonging to the http://www.company.com/examples/purchaseorder namespace by using a namespace prefix such as po. Listing 5-1 shows what the XML Schema Purchase Order example file would look like if I did just that.
On the CD The complete listing for this code excerpt is located in the XMLSPY Handbook Project on this book’s companion CD. Go to the ch5 folder and look for the file Order_5-02.xsd.
Listing 5-1: Changing Namespace Prefix Mappings—Order_5-02.xsd
<-- Order_5-02.xsd, located in XMLSPY Handbook project file, chapter 5 folder --> <schema targetNamespace="http://www.company.com/examples/purchaseorder" xmlns:po="http://www.company.com/examples/purchaseorder" xmlns="http://www. w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified"> <element name="Order"> <complexType> <sequence> <element name="ShippingAddress" type="po:AddressType"/> <element name="BillingAddress" type="po:AddressType"/> <element name="Line-Items"> <complexType> <sequence> <element name="Product" type="po:ProductType" maxOccurs="unbounded"/> </sequence> </complexType> </element> <element ref="po:Note"/> </sequence> </complexType> </element> <complexType name="AddressType"> <sequence> <element name="Street1" type="string"/> <element name="Street2" type="string" minOccurs="0"/> <!-- omitted for brevity --> </sequence> </complexType> <complexType name="ProductType"> <sequence> <element name="Description" type="string"/> <!-- omitted for brevity --> </sequence> </complexType> <element name="Note"> <!-- omitted for brevity --> </element> </schema>
I want to make several important points about the Order_5-02.xsd file show in Listing 5-1:
All the component definitions (globally defined types ProductType and AddressType and global elements Note and Order) still belong to the http://www.company.com/examples/purchaseorder target namespace because I have not made any change in the targetNamespace attribute.
XML Schema vocabulary, such as complexType, element, and sequence, as well as XML Schema–specific attributes such as string and integer belonging to the http://www.w3c.org/2001/XMLSchema namespace (the schema for XML Schemas), no longer need the xsd prefix. They are implicitly prefixed by means of the default namespace.
In the Order element, BillingAddress and ShippingAddress are declared to be of type po:AddressType; the prefix is required.
The key difference between Order_5-02.xsd and Order_5-01.xsd is in how I declare elements as being of a particular complex type defined within the current XML Schema. The Order element declares two elements ShippingAddress and BillingAddress to be of type po:AddressType. In Order_5-02.xsd, the namespace prefix po is absolutely necessary to tell the XML validator where to find the definition of the AddressType component; omitting the prefix in this case will result in the XML validator reporting an error, to the effect of Undefined Value for Type. This explicit namespace association was not necessary in the Purchase Order Schema of Order_5-01.xsd because the unprefixed element type AddressType was prefixed implicitly by means of the default namespace being set to http://www.company.com/examples/purchaseorder.
In a similar fashion, the Order element defined in Order_5-02.xsd declares an element named Note, which is a reference to the global element po:Note, defined elsewhere in the Order_5-02.xsd, belonging to the http://www.company.com/examples/purchaseorder target namespace. Again in this case, omitting the namespace prefix po would result in the validator not being able to reference the specified element type definition required to perform validation. This example illustrates how namespaces convey critical information to the XML validator and controls how a validator locates the XML schema component definitions used in validating both XML Schemas and instance documents.
The easiest way to properly change namespace prefixes for all components referenced or declared within your XML Schema, including the XML Schema vocabulary belonging to the schema for XML Schemas, is to use the Schema Settings window. You can access the Schema Settings window from Schema Design view by choosing Schema Design ? Schema Settings. Try editing the Purchase Order XML Schema of Order_5-02.xsd. Set the http://www.company.com/examples/ purchaseorder namespace to be associated with a mypo (my purchase order) prefix and the http://www.w3c.org/2001/XMLSchema namespace to be associated with the schema prefix, as shown in Figure 5-1.
Figure 5-1: Assigning or changing namespace prefixes in XMLSPY.
In the Schema Settings dialog box, click OK and switch back to Text view. You can see that all element and attributes have been properly re-prefixed (both component declarations and references). The resulting XML Schema with modified namespaces is included in the Order_5-03.xsd file.
Two settings at the top of Figure 5-1 let you specify the default element form and the default attribute form. Currently, the settings are both set to qualified and unqualified, respectively, corresponding to the values for the elementFormDefault and attributeFormDefault attributes located in the schema element. Your choice of either qualified or unqualified locals determines how local elements and attributes should be qualified (that is, how they are to be prefixed) within an instance document meant to conform to your XML Schema. The next two sections help you determine what changes are necessary should you modify these default form settings.
All of the varying Purchase Order Schemas used so far in this chapter define two global elements (Order and Note) and two global complex types (AddressType and ProductType). Within these global definitions are numerous locally declared elements and attributes, such as Street1, City, State, Zip, Quantity, and Price. Setting both the element form default and attribute form default to unqualified implies that it is not necessary to explicitly qualify local elements and attributes as they appear in an instance document. Listing 5-2 shows what an instance document conforming to the Order_5-01.xsd Purchase Order Schema would look like if you set both the elementFormDefault and attributeFormDefault attributes to unqualified:
Listing 5-2: Instance Document Using Unqualified Locals
<?xml version="1.0" encoding="UTF-8"?> <po:Order xmlns:po="http://www.company.com/examples/purchaseorder" ... > <ShippingAddress> <Street1>200 Massachusetts Ave</Street1> <!-- Omitted for brevity --> </ShippingAddress> <BillingAddress> <Street1>250 Columbus Ave.</Street1> <!-- Omitted for brevity --> </BillingAddress> <Line-Items> <Product prod-> <Description>Dishes</Description> <!-- Omitted for brevity --> </Product> </Line-Items> <po:Note><Emphasis>Please</Emphasis> handle items with care.</po:Note> </po:Order>
Notice that only the globally defined constructs, Order and Note, require explicit prefixing; by definition, all global elements must be qualified. Elements such as ShippingAddress, BillingAddress, Line-Items, and Description are all local elements of the Order element and are, therefore, not qualified explicitly. More specifically, their qualification can be inferred because they are local elements of the explicitly qualified Order element that envelops them. Similarly, the Note element must be explicitly qualified because it references a globally defined construct inside the Purchase Order XML Schema (Note is a globally defined element). However, local elements defined underneath Note, such as Emphasis, are local elements and do not require explicit qualification.
It is important to point out that this example works only because I am using a po namespace as opposed to the default namespace. Unqualified locals may not be used in conjunction with the default namespace because the default namespace implicitly qualifies un-prefixed elements!
If an XML Schema specifies that the attributes elementFormDefault and attributeFormDefault are qualified, all elements and attributes (global or local) must be explicitly qualified. Listing 5-3 shows a sample instance document that conforms to a Purchase Order Schema that uses qualified locals. The main difference between this code and the code shown in Listing 5-2 is that all elements and attributes are explicitly qualified in Listing 5-3.
Listing 5-3: Instance Document Using Qualified Locals
<po:Order xmlns:po="http://www.company.com/examples/purchaseorder" ...> <po:ShippingAddress> <po:Street1>300 Newbury St.</po:Street1> <!_--_Omitted for brevity --> </po:ShippingAddress> <po:BillingAddress> <po:Street1>325 Tremont Ave.</po:Street1> <!_--_Omitted for brevity --> </po:BillingAddress> <po:Line-Items> <po:Product po:prod-> <po:Description>Cat Food</po:Description> <!_--_Omitted for brevity --> </po:Product> </po:Line-Items> <po:Note>Postal carrier: <po:Underline>Beware of Cat</po:Underline></po:Note> </po:Order>
In Listing 5-3, all elements and attributes defined in the XML Schema (either global or local) must be explicitly qualified in an instance document. Failure to do so results in a document validation error. You may specify a mixture of qualified and unqualified element and attribute default forms by specifying elementFormDefault equal to qualified and attributeFormDefault equal to unqualified, or the reverse. This would require explicit qualification of either elements or attributes, but not both.
The XML Schema examples I use in this chapter have elementFormDefault set to qualified and attributeFormDefault set as unqualified. Although the choice of what default form to choose is ultimately made by you, the schema author, I believe that this mixed form is intuitive and easily understood. When looking at XML documents containing multiple namespaces, you will immediately know where an element definition is located. This mixed element and attribute default form configuration is also the built-in configuration whenever you create a new XML Schema in XMLSPY.
You can force a particular element or attribute to always appear either qualified or unqualified in an instance document by using the form attribute within the element or attribute definition. For example:
<xsd:element name="MyElement" form="qualified"/>
This defines an element named MyElement that must be explicitly qualified in any instance document that references it.
In the event that you find yourself manually searching and replacing namespace prefixes in an instance document, recall from the discussion of Grid view in Chapter 2 how Grid view enables you to edit XML documents as a whole. You can use Grid view to quickly change namespace prefixes in a document. To change a namespace prefix in Grid view, follow these steps:
Open an instance document in Grid view and expand the document so that you can select the regions of the document to which you want to apply namespace prefix replacements.
Choose XML → Namespace Prefix and type (or modify) the desired namespace.
Click to check the appropriate box to which you want to apply this namespace change—Elements, Attributes, or both.
Click OK, and XMLSPY carries out the namespace replacement operation for you (see Figure 5-2).
Figure 5-2: Changing the namespace prefix of a selected region in Grid view.
All the XML Schema examples that I have shown you so far have been contained in a single file that defined all the schema’s respective components (such as element and attribute definitions). This works fine for simple examples. However, to deal with the complexities of real-world scenarios, you soon realize that it is impossible to build advanced XML Schemas entirely in a single file, just as it would be impossible to build an entire software application in a single file. Therefore, XML Schemas can be defined and assembled from components residing in multiple documents. There are two possible scenarios when working with schemas that are constructed from multiple files:
When two (or more) separate XML Schema files define XML Schema constructs that belong to the same target namespace (or belong to no namespace at all), an XML Schema may include an external component definition.
When an XML Schema belonging to a particular namespace imports one or more schema components defined in different file(s) and belongs to a different namespace, an XML Schema may import an external component definition.
In the following sections, I modify the Purchase Order example to show you how to deal with both situations using XMLSPY.
It is dangerous for two people to be editing the same file at the same time because of the potential for versioning problems. Yet, complex applications are generally developed by teams of programmers, not by individuals working in isolation. So a balance must be found that allows all programmers to continue working without working on the same file, minimizing the risk of versioning problems. Generally, the most practical solution is to separate complex schema files into several files. If the externally defined XML Schema components belong to the same target namespace or do not declare a target namespace, you only have to include the separate files in a master schema. For example, I split up the Purchase Order Schema, defined in Order_5-01.xsd, into two separate schemas under the same namespace: Order_5-05a.xsd, which contains the definitions for Order, ProductType, and Note, and Order_5-05b.xsd, which contains the AddressType complex type definition. The abbreviated code for Order_5-05a.xsd is shown in Listing 5-4. You can find the full listing in the XMLSPY Handbook Project in the ch5 folder.
Listing 5-4: Including an Externally Defined Schema Component—Order_5-05a.xsd
<xsd:schema targetNamespace="http://www.company.com/examples/purchaseorder" xmlns="http://www.company.com/examples/purchaseorder" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified"> <!-- include Address construct From Order_5-05b.xdf --> <xsd:include schemaLocation="Order_5-05b.xsd"/> <xsd:element name="Order"> <-- Abbreviated for Brevity --> </xsd:element> <xsd:complexType name="ProductType"> <xsd:sequence> <xsd:element name="Description" type="xsd:string"/> <-- Abbreviated for Brevity --> </xsd:sequence> <xsd:attribute name="prod-id" type="xsd:integer" use="required"/> </xsd:complexType> <xsd:element name="Note"> <xsd:complexType mixed="true"> <-- Abbreviated for Brevity --> </xsd:complexType> </xsd:element> </xsd:schema>
The listing for Order5_5a.xsd is nearly identical to the original Order5_1.xsd file—the difference is that the AddressType complex type definition has been deleted and replaced with an xsd:include statement, which specifies a path to an external XML Schema:
In turn, the Order_5-05b.xsd file contains the definition for the external AddressType complex type definition that is needed to properly validate Purchase Order instance documents:
<!_--_Order_5-05b.xsd --> <xsd:schema targetNamespace=”http://www.company.com/examples/purchaseorder” xmlns:xsd=”http://www.w3.org/2001/XMLSchema” xmlns=”http://www. company.com/examples/purchaseorder” elementFormDefault=”qualified” attributeFormDefault=”unqualified”> <xsd:complexType name=”AddressType”> <xsd:sequence> <xsd:element name=”Street1” type=”xsd:string”/> <-- Abbreviated for Brevity --> </xsd:sequence> </xsd:complexType> </xsd:schema>
Note that all schema components defined in both Order_5-05a.xsd and Order5_5b.xsd belong to the same target namespace: http://www.company.com/ examples/purchaseorder. xsd:include will not work if the included component definition(s) belong to a different namespace. If no namespace is declared in the external XML Schema, it is treated as though it belongs to the same namespace as the master XML Schema. The Order_5-05a.xsd schema file works identically to the schema defined in Order_5-01.xsd.
To include an XML Schema into another XML Schema using XMLSPY, follow these steps:
Open the XML Schema (the one that includes the external XML Schema) and display it in the Schema Overview page.
Click the Add Global Schema Component button (the left-most button in the top-left corner) and choose Include (see Figure 5-3).
Figure 5-3: Including external XML Schema components belonging to the same namespace.
From the File Picker dialog box, find the XML Schema file that you want to include from your local file system or type the URI that points to where the external schema is located.
As a schema designer, you often need to use schema components, defined in another file, that belong to a separate namespace. This happens often enough that XML Schemas can become very complex, and it is unreasonable to assume that all XML Schema development will start from scratch. It is more likely that as you are designing an advanced XML Schema, you will borrow schema components from other XML Schemas, perhaps from some of the many publicly available industry-standard XML Schemas. This sensible approach enables you to reduce work duplication in schema design. As an example, I present a slightly different Purchase Order Schema that has been divided into two separate schemas under different namespaces: Order_5-06a.xsd and Order_5-06b.xsd. Order_5-06a.xsd contains the definitions for Order, ProductType, and Note, which all belong to the http://www. company.com/examples/purchaseorder target namespace, and Order_5-06b.xsd contains the AddressType complex type definition, which belongs to a new http://www.company.com/examples/address namespace. I highlight the important aspects of the XML Schemas, as shown in Listings 5-5 and 5-6.
Listing 5-5: Importing External XML Schema Components—Order_5-06a.xsd
<xsd:schema targetNamespace="http://www.company.com/examples/purchaseorder" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.company. com/examples/purchaseorder" xmlns:add="http://www.company.com/examples/address" elementFormDefault="qualified" attributeFormDefault="unqualified"> <!-- Import Address Components From order_5-6b.xdf --> <xsd:import namespace="http://www.company.com/examples/address" schemaLocation="Order_5-06b.xsd"/> <xsd:element name="Order"> <xsd:complexType> <xsd:sequence> <xsd:element name="ShippingAddress" type="add:AddressType"/> <xsd:element name="BillingAddress" type="add:AddressType"/> <-- Omitted for brevity --> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:complexType name="ProductType"> <xsd:sequence> <xsd:element name="Description" type="xsd:string"/> <-- Omitted for brevity --> </xsd:sequence> <xsd:attribute name="prod-id" type="xsd:integer" use="required"/> </xsd:complexType> <xsd:element name="Note"> <-- Omitted for brevity --> </xsd:element> </xsd:schema>
Listing 5-6: Imported XML Schema Components—Order_5-06b.xsd
<xsd:schema targetNamespace="http://www.company.com/examples/purchaseorder" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.company.com/ examples/address" elementFormDefault="qualified" attributeFormDefault="unqualified"> <xsd:complexType name="AddressType"> <xsd:sequence> <xsd:element name="Street1" type="xsd:string"/> <xsd:element name="Street2" type="xsd:string" minOccurs="0"/> <xsd:element name="City" type="xsd:string"/> <-- Omitted for brevity --> </xsd:sequence> </xsd:complexType> </xsd:schema>
The Order_5-06a.xsd schema contains three important modifications that are required to properly import a schema component defined in a different file under a separate namespace. First, the namespace of the imported component is introduced inside of the root schema element; in Order_5-06a.xsd, I have associated it with the add prefix (shorthand for Address). Secondly, I have introduced a new element, xsd:import, which has two attributes, namespace and schemaLocation, with values http://www.company.com/examples/address and Order_5-06b.xsd, respectively.
Because both Order_5-06a.xsd and Order_5-06b.xsd are located in the same directory, I simply specify the external filename with no additional path information in the schemaLocation attribute. If the external schema was located elsewhere on the network, you would need to specify the location to the external schema in the form of either a path on the local file system or a URI. Next take a look at the changes in Order_5-06b.xsd, shown in Listing 5-6.
There are no major surprises here. Notice that the AddressType definition belongs to the http://www.company.com/examples/address target namespace. Also, only Global Schema components (such as global elements or named global complex types) may be imported. Here is what an instance document corresponding to the Order_5-06a.xsd schema looks like. I have deliberately not used the default namespace so that you can see where the validator is getting the type definitions:
<po:Order xmlns:po="http://www.company.com/examples/purchaseorder" xmlns:add="http://www.company.com/examples/address" xmlns:xsi="http://www.w3. org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.company.com/ examples/purchaseorder C:\Program Files\altova\XMLSPY\Examples\xmlspyhandbook\Order_5-06a.xsd"> <po:ShippingAddress> <add:Street1>100 Huntington Ave.</add:Street1> <!-- Abbreviated --> </po:ShippingAddress> <po:BillingAddress> <add:Street1>300 Dartmouth St.</</add:Street1> <!-- Abbreviated --> </po:BillingAddress> <po:Line-Items> <po:Product prod-> <!-- Abbreviated --> </po:Product> </po:Line-Items> <po:Note> Next day air express delivery.</po:Note> </po:Order>
The preceding instance document declares both http://www.company.com/ examples/address and http://www.company.com/examples/purchaseorder namespaces. The imported components are properly prefixed as specified by the schema definitions. To import an external XML Schema into your XML Schema using XMLSPY, open the XML Schema (the one that is importing the external XML Schema) and view it in the Schema Overview page. Click the Add Global Schema Component button and choose Import. A File Picker dialog box appears and prompts you to find the XML Schema file you wish to include. Locate the file on your local file system, or type in the URI corresponding to the XML Schema’s location.
By now you have created several variations on the Purchase Order XML Schemas and have created several instance documents that correspond to the various XML Schemas. XMLSPY can automate the manual instance document generation process by autogenerating instance documents and populating them with random (but valid) data. To autogenerate an XML instance document, open the XML Schema or DTD for which you want to generate an instance document in the XMLSPY editing environment so that it is the active document; next, select DTD/Schema → Generate Sample XML File; the window shown in Figure 5-4 appears.
Figure 5-4: Autogenerating an instance document from an XML Schema.
The contents of an autogenerated instance document is completely configurable; you can specify if optional elements and attributes should be generated, how many times repeatable elements should be repeated, and so on. After checking or unchecking the appropriate options, click OK. You can now use the generated instance document as a starting point to test and verify your XML Schema. Of course, you’ll probably want to manually add additional sample data to the autogenerated instance document to adequately test the boundaries of the value spaces of your XML Schema data types; still, the autogenerated instance files can certainly serve as a testing starting point.