Day 15
Working with Namespaces

In yesterday’s lesson, you learned to work with multiple XML sources. By loading additional documents into a variable, you can use one stylesheet to operate on separate datasets in different files. This capability is very useful if different files contain related data.

One of the problems of working with multiple data structures is that their elements may have the same name but a different meaning. In most cases, that also means that they need to be processed and displayed differently. This is where XML namespaces come in, which is the key topic of today’s lesson. Throughout this book, you have informally been working with namespaces, but the time has come to bring this topic more to the foreground and formalize it.

When you process an XML document with one or more namepaces, the stylesheet needs to be aware of those namespaces and address the data in the source document differently from documents that don’t use namespaces. Creating documents that use namespaces from XSLT is also different. These topics are covered today.

In today’s lesson, you will learn the following:

• What XML namespaces are

• The benefits of namespaces

• The relationship between namespaces, DTDs, and XML Schemas

• How to utilize namespace information

• How to change and remove namespace information

Understanding Namespaces

NEW TERM

When you’re working with multiple source XML documents, elements and attributes with the same name can collide. Names are said to collide if the name is the same, but the meaning is different.

The problem with colliding elements and attributes is that because their meaning is different, they probably should be handled differently. However, because the names are the same, there is no way to figure out which meaning the element or attribute has. Namespaces are a mechanism to avoid naming collisions, so elements with the same name but with different meaning can be treated as different and, as such, be processed differently. This section takes a closer look at the hows and whys of namespaces.

Namespaces Explained

A namespace is a means to keep different vocabularies of elements apart. The mechanism for this is quite simple and is implemented in a very handy way. How namespaces work exactly is explained later in this section. First, let’s look a little closer at XML vocabularies.

Understanding XML Vocabularies

Different XML structures use different vocabularies of elements. HTML (or rather XHTML, which is XML) has a vocabulary containing all the common elements in HTML. Any unknown element is not understood by the browser and basically is ignored. This is just like the language you speak. If somebody uses a word that is not in your vocabulary, you don’t know what the person is saying to you. Many languages use the same structure (grammar), but different words, and this is the same with XML. Applications that have the same function would benefit from using the same XML vocabulary, just like people benefit from speaking the same language. To make it easier for applications to speak the same language, there are some vocabularies for common applications, some of which are listed here:

• Synchronized Multimedia Integration Language (SMIL), which is used to integrate a set of independent multimedia objects into a synchronized multimedia presentation. See http://www.w3.org/TR/REC-smil/.

• Scalable Vector Graphics (SVG), which is a language used to describe two-dimensional vector and mixed vector/raster graphics in XML. See http://www.w3.org/TR/2001/REC-SVG-20010904/.

• Simple Object Access Protocol (SOAP), which is used for communication between two systems. Most often the server executes some function based on the information transmitted to it and sends back a response. See http://www.w3.org/TR/SOAP/.

• Web Services Description Language (WSDL), which is basically used as a sort of directory for finding services (such as SOAP) on a network (specifically the Internet). See http://www.w3.org/TR/wsdl.

• XML-based User Interface Language (XUL), which is used to define user interfaces in a Web browser. See http://www.mozilla.org/xpfe/xptoolkit/xulintro.html.

• XML Metadata Interchange (XMI), which is an effort to combine the Unified Modeling Language (UML) defined by OMG (http://www.omg.org) and XML. See http://www-4.ibm.com/software/ad/library/standards/xmi.html.

Many more XML vocabularies are out there and under development for all kinds of applications; however, the listed vocabularies are more visible and widely available because they are endorsed by the W3 Consortium, Mozilla, or the Object Management Group (OMG). A vocabulary that you are by now very familiar with, but which is not in the preceding list, is XSLT. You also can define your own vocabularies using DTDs or XML Schemas.

Vocabulary Collisions

When you have different vocabularies, you might have elements or attributes that have the same name as used in another vocabulary. When these elements or attributes have a different meaning, this can be a cause for problems. For example, in some of the samples used in this book, the element model is used to denote a car model:

<model name="Focus" manufacturer="Ford" year="2000" />

However, the following model element has a totally different meaning:

<model name="Cindy Crawford" height="5'9-1/2" nationality="USA" />

Still, both of the preceding elements are well-formed XML. The only way you might be able to tell them apart is by their ancestor element (s) and by the values of the attributes and attribute names. The problem is that elements may even have the same attributes, and as soon as you start checking the values, you bring semantic information into your stylesheets. In addition, your stylesheet can work only with the selected set of values, and any new values are considered as elements from a different vocabulary. The bottom line is that if there is no way to tell apart the vocabularies, you end up with your stylesheet producing the wrong output.

Using Namespaces

Namespaces solve the problem of colliding vocabularies. A namespace consists of two parts: a namespace name and a namespace prefix. The namespace prefix is a prefix added to an element or attribute name. The namespace prefix and element or attribute name are separated by a colon—for instance, xsl:template, car:model, and fashion:model. As you can see from the last two elements, a namespace prefix allows you to treat the two model elements as different elements.

A namespace can be declared at any point in an XML document; however, it should be declared in the root element of the tree fragment using the namespace, or in any of the ancestor elements of that root element. In an XSLT document, it therefore needs to be declared in the root element of the stylesheet, as the namespace is being used already by the root element itself, either by xsl:stylesheet or the alternative root element xsl:transform. For exactly this reason, the root element in XSLT always looks like this:

<xsl:stylesheet version=″“1.0”"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

The xmlns attribute denotes a namespace declaration, with the namespace prefix being used following the colon (in this case, xsl). The attribute’s value is the namespace name being used. The namespace name should uniquely identify the namespace. If in two different documents a namespace is declared with the same namespace name, the elements and attributes in that namespace are the same. It doesn’t matter whether the namespace prefix used in the different documents is different. An XML parser or processor treats different prefixes as the same namespace when their namespace name is the same. Therefore, Listing 15.1 and Listing 15.2 are identical.

LISTING 15.1 Stylesheet Using xsl Namespace

<?xml version=″“1.0”" encoding="UTF-8"?>
<xsl:stylesheet version=″“1.0”"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="/">
    <xsl:apply-templates />
  </xsl:template>
</xsl:stylesheet>

LISTING 15.2 Stylesheet Using abc Namespace

<?xml version=″“1.0”" encoding="UTF-8"?>
<abc:stylesheet version=″“1.0”"
  xmlns:abc="http://www.w3.org/1999/XSL/Transform">

  <abc:template match="/">
    <abc:apply-templates />
  </abc:template>
</abc:stylesheet>

ANALYSIS

As far as any parser or processor is concerned, Listing 15.1 and Listing 15.2 are semantically the same, because although the namespace prefixes xsl and abc differ significantly, the namespace name used in the namespace declaration is the same. Therefore, they are treated as the same namespace, and xsl:template and abc:template are the same elements. It is important that you understand the distinction between the namespace, the namespace name, and the prefix used for that namespace within an XML document. The namespace is defined by the namespace identifier; the prefix is just a means to identify the elements and attributes of a namespace within a document.

Tip

Although namespaces are semantically the same when they use the namespace name, using different prefixes for namespaces across documents is not a good idea. It is much better to keep the prefix of a namespace consistent across documents. This way, you can more easily work with them in large projects or even across projects.

The namespace name has to be a unique identifier because you need to be sure that, if the namespace name is used in two documents, the namespace and vocabulary used are actually the same for those documents. This is why the namespace name is defined as a URI because a URI points to a globally unique location and is therefore a globally unique identifier. The URI is commonly a URL pointing to a document or directory on a specific server. This more or less guarantees that the URI is unique and that it is persistent. You would expect the URI to yield a document defining the vocabulary such as a DTD or XML Schema. This, however, is by no means mandatory, and you will often find that, if you use the namespace name in a browser, you will get a 404 Not Found error because namespace declarations are just namespace declarations by design. The XML Namespace Recommendation (http://www.w3.org/TR/1999/REC-xml-names-19990114/) specifically says that it is not the goal of the namespace declaration to retrieve a DTD or Schema. It is reasonable to assume that this also means that the namespace declaration is not meant to be used to validate the XML using the specified namespace. That said, MSXML is one of the parser/processors that supports this functionality anyway.

The Benefits of Namespaces

The first and foremost task of namespaces is to keep apart different vocabularies. In a single document, using a single vocabulary might not make a lot of sense to you, but consider what happens when you combine documents that may or may not have different vocabularies. If you don’t define namespaces in each document, you might get into the same trouble as discussed earlier. The point is that XML resources will likely be a part of a larger system. In fact, on the Internet, it is likely that someone will use data from another XML document, even if that wasn’t intended. So, it is useful to think beyond what you’re doing and to think beyond what you’re doing now. Others might want to build on what you have created, or you might want to expand on what you have already done. Without namespaces, you may hit a brick wall somewhere down the road.

Because you can mix vocabularies within one XML document, you can mix information of the different vocabularies as well. The elements and hierarchies in the document do not necessarily have to be of the same namespace. This is handy if you want to keep data together but want to be able to process it separately. Listing 15.3 shows an XML source that uses this concept.

LISTING 15.3 XML Document Mixing Namespaces

<?xml version=″“1.0”" encoding="UTF-8"?>
<shop:basket xmlns:shop="http://www.example.com/xmlns/shop"
             xmlns:product="http://www.example.com/xmlns/products">
  <product:product product:ID="234" product:description="Bordeaux"
               product:price="50.00" shop:quantity="1"/>
  <product:product product:ID="123" product:description="Brie"
               product:price="99.95" shop:quantity="3"/>
</shop:basket>

ANALYSIS

Listing 15.3 uses two namespaces: one uses the prefix shop; and the other, the prefix product. The namespaces are used mixed with one another. The benefit of this approach is that the attributes belonging to the product namespace can be processed separately if necessary. You can address them as a group by using the expression @product:*, so it is not relevant which attributes are there. These data items are all related and stay the same over a period of time. The data in the shop namespace is specific to one user and will change often. You could also envision adding the user’s address information to the document, possibly using a different namespace. This way, the user’s shopping information is grouped into one document that can be used while the user is shopping, to do the checkout, for shipping and handling, for invoicing, and so on. Each process needs only part of the information, but it needs to be grouped in some way to be relevant. You don’t want to have a separate invoice, a different document for shipping, and so on.

The Drawbacks of Namespaces

Namespaces are handy, but they add to the complexity of a document, specifically when it contains more than one namespace.

Note

In a document, you can define the default namespace by using xmlns=“some URI”. Elements without a prefix are then of that namespace. This definition is particularly useful in documents that use only one namespace because it cuts down on typing.

A more complex XML document means that it takes longer to create and, even worse, that any XSLT stylesheet also becomes more complex. A stylesheet needs to declare the namespace just like the XML source the stylesheet is operating on. Fortunately, the namespace prefix may be different than that used in the source document. As long as the namespace name is the same, the stylesheet will work fine with the XML source, and the data in the XML source will be treated as if it has the prefix defined in the stylesheet. This way namespaces in two different documents with the same prefix can be treated by the stylesheet as if they have different prefixes. So, if your stylesheet needs to work on both documents, it has a way to distinguish the two namespaces.

NEW TERM

When you’re mixing vocabularies using namespaces, validating the XML becomes much harder. Although, technically, this situation doesn’t have to do with namespaces, but rather with validation, it is very much related. The point is that if you’re validating a document that uses multiple namespaces, it is likely that you are validating against multiple DTDs or Schemas. If the validation fails on any one DTD or Schema, the entire operation is canceled and the document is loaded and transformed. This is specifically a problem if you’re using closed DTDs or Schemas. A closed DTD or Schema cannot be extended upon. Its vocabulary and hierarchy are strictly defined and enforced at validation time.

That a closed DTD or Schema will not work properly isn’t really a surprise. Because it is closed, you shouldn’t extend it. Mixing it up with other elements from other namespaces to the parser means extending it, so that is illegal by the very nature of the DTD or Schema. Nevertheless, this situation is not uncommon and not the only cause for problems. DTDs and Schemas expect certain elements and attributes in certain places. If they aren’t there, validation fails and so does everything else. When you’re mixing namespaces, you might be violating rules in a DTD or Schema just because it has an element of another namespace that the parser didn’t expect. This means that the parser has no choice but to abort. Unless the parser or processor you’re working with gives details about the validation error, tracking it down can be very hard. Also, if you look at the result in Internet Explorer, you might not see an error, but just a document that looks different than it is supposed to.

Namespaces, DTDs, and Schemas

It is very natural to think that namespaces and vocabulary definitions with a DTD or XML Schema are linked together. In fact, they are not. A namespace is just meant to keep vocabularies apart. Whether you validate a source document with a DTD or Schema is of no concern to the namespace, nor is the namespace concerned with validating documents itself.

Validation by a DTD is fairly simple. If a DTD is associated with an XML source using a DOCTYPE declaration, a validating parser will validate the document when it is loaded. Most parsers/processors implement this behavior.

XML Schema is fairly new, so you should check whether the parser/processor you use supports validation against a Schema. The parser that currently comes with Saxon, for instance, doesn’t support XML Schema; MSXML 4.0 does, however. MSXML 3.0, which was released before the XML Schema Recommendation, supports a mechanism to validate against a Schema that is different from the mechanism defined in the XML Schema Recommendation. An example of validation following the recommendation is shown in Listing 15.4.

LISTING 15.4 Validation Information for XML Schema

1: <car:model
2:   xmlns:car="http://www.example.com/xmlns/car"
3:   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4:   xsi:schemaLocation="http://www.example.com/xmlns/car
5:   http://www.example.com/xmlns/car/car.xsd"
6:   name="Golf" manufacturer="Volkswagen" year="1999" />

ANALYSIS

The document in Listing 15.4 has a single element. This point is not very significant, but the namespace declarations and their use are. On line 2, the namespace for the car prefix is declared. On line 3, the xsi prefix is declared for the namespace name http://www.w3.org/2001/XMLSchema-instance. This namespace is the one used for XML Schema validation. Just like the namespace used for XSLT, this namespace must be associated with the given URI, or it will not be regarded as the XML Schema validation namespace. This namespace is used on lines 4 and 5 to define the Schema that should be used to validate the XML document. Where the Schema is located is defined using the xsi:SchemaLocation attribute, which first holds the namespace name (line 4) and then the location of the Schema to be associated with that namespace name (line 5). The two URIs are separated by whitespace. If you’re working with an XML Schema– compliant parser/processor, such as MSXML 4.0, Listing 15.4 will be validated against the Schema located at http://www.example.com/xmlns/car/car.xsd.

Note

The car.xsd Schema is not provided here because Schemas are beyond the scope of this book. Schemas are discussed here in the context of their relationship with namespaces.

In MSXML 3.0 and higher, you also can validate against an XML Schema by adding x-Schema in front of the namespace name, like this:

xmlns:car="x-Schema:http://www.example.com/xmlns/car/car.xsd"

The drawback of this method is that the namespace name and the Schema location are one and the same, which, as explained earlier, is not the best solution.

Caution

Validation information should be incorporated only in the source document. A common mistake, particularly with the MSXML Schema validation method, is to include this information in the stylesheet namespace declaration as well. The result is that your stylesheet is not valid and that, at the very least, your output will be incorrect.

Processing XML Sources with Namespaces

In the preceding section, you learned all the theory surrounding namespaces. The next step is, of course, learning about actually using namespaces. The first thing you need to know is how to process documents containing one or more namespaces. Listing 15.5 is an example of such a document.

LISTING 15.5 Sample XML Source with Namespace

1:  <?xml version=″“1.0”" encoding="UTF-8"?>
2:  <car:cars xmlns:car="http://www.example.com/xmlns/car">
3:    <car:model name="Golf" manufacturer="Volkswagen" year="1999" />
4:    <car:model name="Camry" manufacturer="Toyota" year="1999" />
5:    <car:model name="Focus" manufacturer="Ford" year="2000" />
6:    <car:model name="Civic" manufacturer="Honda" year="2000" />
7:    <car:model name="Prizm" manufacturer="Chevrolet" year="2000" />
8:    <car:model name="Celica" manufacturer="Toyota" year="2000" />
9:    <car:model name="Mustang" manufacturer="Ford" year="2001" />
10:   <car:model name="Passat" manufacturer="Volkswagen" year="2001" />
11:   <car:model name="Accord" manufacturer="Honda" year="2002" />
12:   <car:model name="Corvette" manufacturer="Chevrolet" year="2002" />
13:</car:cars>

Note

You can download the sample listings in this lesson from the publisher’s Web site.

ANALYSIS

Listing 15.5 contains an XML source using one namespace. The namespace defined by the http://www.example.com/xmlns/car namespace name appears on line 2. In this document, it is given the prefix car. Because the root element itself uses the namespace, the namespace needs to be declared as part of the root element. Otherwise, the namespace could be declared when it is first used in the document. You can always declare all namespaces in the root element if you like. Note that the element name uses the namespace, but the attributes do not. This fact is very important because it means that no namespace is associated with the attributes (unless a default namespace has been declared). Any template matching attributes with the car namespace will not match the attributes in Listing 15.5.

If you want to associate a namespace with attributes, you have to add a namespace prefix as well, as shown in Listing 15.3 earlier. Unless you’re sure that your attributes are either unique or the same as attributes with the same name in other vocabularies, you should add a namespace to the attributes as well, although doing so makes your XML source larger (and more work to type). As I said earlier, you can declare a default namespace to be associated with elements and attributes that use no prefix.

When you have an XML source using a namespace, your stylesheet needs to process the elements as part of that namespace. This means that it needs to have knowledge of that namespace and possibly process nodes according to their namespace. To give the stylesheet knowledge about a namespace, you have to declare that namespace in the stylesheet, just as you did in the source document, as shown in Listing 15.6.

LISTING 15.6 Stylesheet Using Namespace from Listing 15.5

1:  <?xml version=″“1.0”" encoding="UTF-8"?>
2:  <xsl:stylesheet version=″“1.0”"
3:    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
4:    xmlns:auto="http://www.example.com/xmlns/car">
5:
6:    <xsl:output method="text" encoding="UTF-8" />
7:    <xsl:strip-space elements="*" />
8:
9:    <xsl:template match="/">
10:     <xsl:apply-templates />
11:   </xsl:template>
12:
13:   <xsl:template match="auto:cars">
14:     <xsl:apply-templates />
15:   </xsl:template>
16:
17:   <xsl:template match="auto:model">
18:     <xsl:value-of select="@manufacturer" />
19:     <xsl:text> </xsl:text>
20:     <xsl:value-of select="@name" />
21:     <xsl:text>  (</xsl:text>
22:     <xsl:value-of select="@year" />
23:     <xsl:text>)&#A;</xsl:text>
24:   </xsl:template>
25: </xsl:stylesheet>

ANALYSIS

Because Listing 15.6 operates on an XML source using namespaces, it needs to be aware of those namespaces. In this case, only one namespace is used, and it is declared on line 4. Note that the namespace name is the same as in Listing 15.5, but the prefix is different. The processor will translate the prefix used in the source XML to the prefix used in the stylesheet. This way, if another document is used with another namespace but the same prefix, it can just be defined with another prefix in the stylesheet, so they can’t be in each other’s way. The elements that are associated with the namespace in Listing 15.5 are matched on lines 13 and 17 by two different templates. As you can see, those templates use the namespace prefix declared on line 4. The attributes of the model element, on the other hand, are selected without using a namespace prefix because they aren’t associated with a namespace. If line 20 used @auto:name, it would select nothing because that attribute is not available, which shows you that there really is no namespace associated with the attribute. The output of Listing 15.6 is shown in Listing 15.7.

OUTPUT

LISTING 15.7 Result from Applying Listing 15.6 to Listing 15.5

Volkswagen Golf  (1999)
Toyota Camry  (1999)
Ford Focus  (2000)
Honda Civic  (2000)
Chevrolet Prizm  (2000)
Toyota Celica  (2000)
Ford Mustang  (2001)
Volkswagen Passat  (2001)
Honda Accord  (2002)
Chevrolet Corvette  (2002) 

ANALYSIS

Although Listing 15.6 works with namespaces, the output in Listing 15.7 is just plain text, which is familiar from earlier lessons.

Getting Namespace Information

When you’re using namespaces, you can get information about the namespace of an element in different ways. First, you can get the namespace prefix of the current element by using the substring-before () function. So, the following line of code would yield the prefix of the current element:

substring (.,':')

You also can very easily check whether elements are of a certain namespace by using the following line of code:

<xsl:if test="self::car:*" xmlns:car="http://www.example.com/xmlns/car">

The body of the preceding code would be executed if the current element is part of the http://www.example.com/xmlns/car namespace. Note that the namespace is declared in the xsl:if element instead of in the root element. If you just want to get the namespace name of the namespace associated with the current element, you can also use the namespace-uri () function, which returns the URI that is used as the namespace name. Because you can test the namespace of an element just by using its prefix, this function is not very useful in most stylesheets. However, when you’re doing some kind of reporting or testing, this function may be useful. Listing 15.8 uses this function.

LISTING 15.8 Stylesheet Using the namespace-uri () Function

1:  <?xml version=″“1.0”" encoding="UTF-8"?>
2:  <xsl:stylesheet version=″“1.0”"
3:    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
4:    xmlns:auto="http://www.example.com/xmlns/car">
5:
6:    <xsl:output method="text" encoding="UTF-8" />
7:
8:    <xsl:template match="/">
9:      <xsl:value-of select="namespace-uri (child::*)" />
10:   </xsl:template>
11: </xsl:stylesheet>

ANALYSIS

The code in Listing 15.8 does nothing more than output the namespace name (URI) of the root node of the document being processed. It works only on another stylesheet or a document having a root node associated with the http://www.example.com/xmlns/car namespace because only these namespaces are declared in this stylesheet on lines 3 and 4. Line 9 uses the namespace-uri () function to output the namespace name of the root element, selected using the child::* expression, which selects any child node of the document root. If the root element of the source document is associated with the http://www.example.com/xmlns/car namespace, that namespace is the output. Otherwise, the result is empty.

Inserting and Removing Namespaces

In the preceding sections, you learned how to work with a document that uses namespaces. As yet, you can use only documents that use namespaces. Creating a document that uses a namespace or changing the namespace that you got from the output is a different matter.

Inserting Nodes with Namespaces

Inserting namespaces into the output document is remarkably easy. You can just insert the node with the namespace prefix and colon, and you’re done. The only requirement is that the namespace is declared within the stylesheet. It is a good idea to declare all the namespaces in the root element of the stylesheet so that you always can quickly see which namespaces have been declared and with which prefix. A nice way to have a look at inserting namespaces is to transform an XML source that doesn’t have namespaces into one that does have them. Listing 15.9 shows the XML source to be transformed.

LISTING 15.9 XML Source Without a Namespace

<?xml version=″“1.0”" encoding="UTF-8"?>
<cars>
  <car name="Golf" manufacturer="Volkswagen" year="1999" />
  <car name="Camry" manufacturer="Toyota" year="1999" />
  <car name="Focus" manufacturer="Ford" year="2000" />
  <car name="Civic" manufacturer="Honda" year="2000" />
  <car name="Prizm" manufacturer="Chevrolet" year="2000" />
</cars>

Listing 15.9 is one of the familiar variants of car information documents used throughout this book. Now, however, it will be changed into a document that uses a namespace for the elements and attributes. Listing 15.10 is the stylesheet that will transform Listing 15.9.

LISTING 15.10 Stylesheet Creating a Document with a Namespace

1:  <?xml version=″“1.0”" encoding="UTF-8"?>
2:  <xsl:stylesheet version=″“1.0”"
3:    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
4:    xmlns:car="http://www.example.com/xmlns/car">
5:
6:    <xsl:output method="xml" encoding="UTF-8" indent="yes" />
7:    <xsl:strip-space elements="*" />
8:
9:    <xsl:template match="/">
10:     <xsl:apply-templates />
11:   </xsl:template>
12:
13:   <xsl:template match="cars">
14:     <car:cars>
15:       <xsl:apply-templates />
16:    </car:cars>
17:   </xsl:template>
18:
19:   <xsl:template match="car">
20:     <car:car>
21:       <xsl:apply-templates select="@*" />
22:     </car:car>
23:   </xsl:template>
24: 
25:   <xsl:template match="@*">
26:     <xsl:attribute name="car:{name ()}">
27:       <xsl:value-of select="." />
28:     </xsl:attribute>
29:   </xsl:template>
30: </xsl:stylesheet>

ANALYSIS

Listing 15.10 will transform Listing 15.9 into a document using a namespace (shown in Listing 15.11). Because the stylesheet basically copies the existing nodes, it doesn’t have a lot of exotic functionality. The template matching the cars element on line 13 inserts the element into the output as car:cars on line 14. This is possible because that namespace is declared on line 4 of the stylesheet. If it weren’t, line 14 would generate an error. The same thing is done for the car elements in the template starting on line 19. On line 21, this same template initiates processing for all the attributes of the car element. The attributes are processed by the template on line 25. Line 26 inserts the attribute, preceded by the namespace prefix. As you can see, the name is constructed from the prefix and a dynamic part using the name () function to get the original name of the attribute from the source document.

OUTPUT

LISTING 15.11 Result from Applying Listing 15.10 to Listing 15.9

1: <?xml version=″“1.0”" encoding="UTF-8"?>
2: <car:cars xmlns:car="http://www.example.com/xmlns/car">
3:   <car:car car:name="Golf" car:manufacturer="Volkswagen" car:year="1999"/>
4:   <car:car car:name="Camry" car:manufacturer="Toyota" car:year="1999"/>
5:   <car:car car:name="Focus" car:manufacturer="Ford" car:year="2000"/>
6:   <car:car car:name="Civic" car:manufacturer="Honda" car:year="2000"/>
7:   <car:car car:name="Prizm" car:manufacturer="Chevrolet" car:year="2000"/>
8: </car:cars>

ANALYSIS

Listing 15.11 uses the namespace that was inserted in Listing 15.10. As you can see, the namespace is declared on line 2 and then used for each element and attribute.

Changing Namespaces

With XSLT, you can change the representation of a namespace as used in a stylesheet to something different for use in the output. At first glance, changing the namespace seems totally unnecessary, but think about creating stylesheets with a stylesheet. If you want to output an XSLT element like xsl:stylesheet, you can’t insert it into the stylesheet as such. If you did that, it would be interpreted as an XSLT element in the stylesheet, and not as an element to be sent to the output. The solution is to use a temporary alias for the XSLT elements you want to output. If you use axsl:stylesheet and declare a separate namespace for it, the stylesheet doesn’t have a problem because it can make a distinction between xsl and axsl.

The preceding approach still poses one problem, however. The namespace associated with the alias must be different in the stylesheet, which means that it also will be different in the output. Because XSLT relies on the namespace name to identify it as XSLT, the namespace associated with the output would not identify it as XSLT. This is where the xsl:namespace-alias element comes in. It allows you to map a temporary alias to another namespace in the stylesheet. When the output is generated, the namespace prefix is substituted for the one used in the stylesheet, but more important, the namespace name associated with it is substituted as well. Listing 15.12 shows how to use a namespace alias.

LISTING 15.12 Stylesheet Using a Namespace Alias

1:  <?xml version=″“1.0”" encoding="UTF-8"?>
2:  <xsl:stylesheet version=″“1.0”"
3:    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
4:    xmlns:out="temp">
5:
6:    <xsl:output method="xml" encoding="UTF-8" indent="yes" />
7:    <xsl:strip-space elements="*" />
8:    <xsl:namespace-alias result-prefix="xsl" stylesheet-prefix="out" />
9:
10:   <xsl:template match="/">
11:     <out:stylesheet version=″“1.0”">
12:       <out:value-of select="." />
13:     </out:stylesheet>
14:   </xsl:template>
15: </xsl:stylesheet>

ANALYSIS

Listing 15.12 creates another stylesheet. To do that, a temporary namespace is declared on line 4. This listing uses out as a namespace prefix and temp as a namespace name, but they could be nearly anything, as long as they aren’t the same as any other namespace declarations in the stylesheet. Line 8 maps the out namespace to the xsl namespace. The result-prefix attribute defines the namespace to be used in the output, and the stylesheet-prefix attribute defines the namespace used within the stylesheet. When the output is created, the xsl namespace prefix is used, along with the namespace name used on line 3. On lines 11 through 13, elements are inserted using the out namespace prefix. Note that the elements inserted are valid XSLT elements with the correct attributes. When the output is created (using any XML source), the result should look like Listing 15.13.

OUTPUT

LISTING 15.13 Result from Listing 15.12

1: <?xml version=″“1.0”" encoding="UTF-8"?>
2: <xsl:stylesheet version=″“1.0”"
Image     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
3: <xsl:value-of select="." />
4: </xsl:stylesheet>

ANALYSIS

Listing 15.13 is the exact stylesheet that was created by Listing 15.12, containing the elements inserted. Because of the xsl:namespace-alias element, the namespace prefix used is xsl. In addition, line 2 shows that the namespace name used is the URI needed to associate the xsl prefix with XSLT, so a processor will actually regard Listing 15.12 as a stylesheet. The temporary namespace declared in Listing 15.12 is nowhere to be found, which is as it should be.

The behavior for the different processors is not the same. The XSLT specification states that the only requirement is that the namespace name is correct, although the intention is likely to be as shown in Listing 15.13. MSXML produces this output, but Saxon and Xalan produce different output from MSXML and each other. The output for Saxon is shown in Listing 15.14 and for Xalan in Listing 15.15.

OUTPUT

LISTING 15.14 Result from Listing 15.12 with Saxon

<?xml version=″“1.0”" encoding="UTF-8"?>
<out:stylesheet xmlns:out="http://www.w3.org/1999/XSL/Transform"
Image  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version=″“1.0”">
  <out:value-of select="."/>
</out:stylesheet>

LISTING 15.15 Result from Listing 15.12 with Xalan

<?xml version=″“1.0”" encoding="UTF-8"?>
<out:stylesheet xmlns:out="http://www.w3.org/1999/XSL/Transform"
Image  version=″“1.0”">
<out:value-of select="."/>
</out:stylesheet>

ANALYSIS

Listings 15.14 and 15.15 are just as valid as Listing 15.13. Both use the alias prefix in Listing 15.12 for the output and attach the original namespace to that prefix. Because the namespace name defines the namespace, and not the prefix, the output is semantically the same. In addition, Saxon adds the original namespace, although this is completely superfluous.

Note

Listings 15.13, 15.14, and 15.15 are the same for all intents and purposes. Don’t be fooled by the different appearances. Other processors may produce even different output, but the output is always semantically the same.

The sample in Listing 15.12 is, of course, not very practical because it creates a style-sheet from a stylesheet and does nothing to change the resulting stylesheet according to some source document. It does, however, serve its purpose as a short example with a namespace alias. You can use the same method to create a stylesheet based on some XML source.

Removing Namespaces

If you declare a namespace in a stylesheet, that namespace declaration is copied to the output if you’re generating XML or HTML. This also is the case if a namespace isn’t even used in the output document. The only exception is the XSLT namespace, which is not sent to the output, unless you have used xsl:namespace-alias to alias the XSLT namespace.

Namespace declarations usually don’t bite when they are included in the output but not used. It doesn’t look pretty, though, if a document is processed a few times, and each time a new, not used, namespace declaration is added. Fortunately, you can remove namespace declarations by using the exclude-result-prefixes attribute of the xsl:stylesheet element. Its value needs to be a whitespace-separated list of prefixes of the namespaces you want to exclude from the output. Any namespace that is not used in the output is completely omitted from it, including its declaration. If a namespace is used in the output and is listed in the exclude-result-prefixes attribute, the namespace is declared in the output anyway. The most common use for this attribute is creating HTML documents, which Listing 15.16 demonstrates.

LISTING 15.16 Stylesheet Creating an HTML Document with a Table from Listing 15.11

1:  <?xml version=″“1.0”" encoding="UTF-8"?>
2:  <xsl:stylesheet version=″“1.0”"
3:    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
4:    xmlns:car="http://www.example.com/xmlns/car">
5:
6:    <xsl:output method="html" encoding="UTF-8" indent="yes" />
7:    <xsl:strip-space elements="*" />
8:
9:    <xsl:template match="/">
10:     <html>
11:     <body>
12:       <xsl:apply-templates />
13:     </body>
14:     </html>
15:   </xsl:template>
16:
17:   <xsl:template match="car:cars">
18:     <table>
19:       <xsl:apply-templates />
20:     </table>
21:   </xsl:template>
22:
23:   <xsl:template match="car:car">
24:   <tr>
25:     <xsl:apply-templates select="@*" />
26:   </tr>
27:   </xsl:template>
28:
29:   <xsl:template match="@*">
30:     <td><xsl:value-of select="." /></td>
31:   </xsl:template>
32: </xsl:stylesheet>

ANALYSIS

Listing 15.16 creates an HTML document with a table listing the car:car elements in Listing 15.11. Essentially, you have done the same thing before, except for the namespace used in Listing 15.11 and declared on line 4 of the stylesheet. Listing 15.16 yields the result in Listing 15.17.

OUTPUT

LISTING 15.17 Result from Applying Listing 15.16 to Listing 15.11

<html xmlns:car="http://www.example.com/xmlns/car">
   <body>
      <table>
         <tr>
            <td>Golf</td>
            <td>Volkswagen</td>
            <td>1999</td>
         </tr>
         <tr>
            <td>Camry</td>
            <td>Toyota</td>
            <td>1999</td>
         </tr>
         <tr>
            <td>Focus</td>
            <td>Ford</td>
            <td>2000</td>
         </tr>
         <tr>
            <td>Civic</td>
            <td>Honda</td>
            <td>2000</td>
        </tr>
        <tr>
           <td>Prizm</td>
           <td>Chevrolet</td>
           <td>2000</td>
        </tr>
     </table>
  </body>
</html>

ANALYSIS

Listing 15.17 is just a plain HTML document. The only exception is that the first line contains a namespace declaration for the car prefix. Clearly, that namespace has no purpose because the browser ignores it and just displays the HTML. If you added exclude-result-prefixes="car" to the xsl:stylesheet element in Listing 15.16, the namespace declaration in Listing 15.17 would not be there, resulting in a perfect HTML document.

Removing Namespace Prefixes

Earlier you learned how to create a document using namespaces from one that didn’t. The last subject of this lesson goes the other way around, removing namespaces and matching nodes on just their name, without the namespace prefix. Basically, you can choose from two methods to remove namespace prefixes. The first method uses the substring-after () function to get the name of a node without the namespace prefix. You can, however, use an easier method, so this method will not be discussed further. Using the local-name () function, you can get the name of a node without the prefix, and as such, this function is also suited to do name tests in expressions. This capability is useful if you have nodes in different namespaces that need to be matched by the same template, although that contradicts the issue of separating vocabularies using namespaces. You can imagine that different namespaces use some of the same elements and attributes, such as names. When that happens, you can use the same template to process these elements, as shown by the example in Listing 15.18.

LISTING 15.18 Sample XML Source with Multiple Namespaces

1:  <?xml version=″“1.0”" encoding="UTF-8"?>
2:  <car:cars xmlns:car="http://www.example.com/xmlns/car"
3:            xmlns:m="http://www.example.com/xmlns/manufacturer">
4:    <car:models>
5:      <car:model car:name="Golf" m:id="VW" car:year="1999" />
6:      <car:model car:name="Camry" m:id="TY" car:year="1999" />
7:      <car:model car:name="Focus" m:id="FO" car:year="2000" />
8:      <car:model car:name="Civic" m:id="HO" car:year="2000" />
9:      <car:model car:name="Prizm" m:id="CV" car:year="2000" />
10:   </car:models>
11:   <m:manufacturers>
12:     <m:manufacturer m:id="VW" m:name="Volkswagen" m:country="Germany" />
13:     <m:manufacturer m:id="TY" m:name="Toyota" m:country="Japan" />
14:     <m:manufacturer m:id="FO" m:name="Ford" m:country="USA" />
15:     <m:manufacturer m:id="CV" m:name="Chevrolet" m:country="USA" />
16:     <m:manufacturer m:id="HO" m:name="Honda" m:country="Japan" />
17:   </m:manufacturers>
18: </car:cars>

ANALYSIS

Listing 15.18 mixes two namespaces: one for cars and one for manufacturers. These namespaces are declared on lines 2 and 3, and are used by both elements and attributes. Because the manufacturers are related to the cars, the common root element is of the car namespace. Note that the elements have the name attribute in common. Listing 15.19 uses this knowledge.

LISTING 15.19 Stylesheet Using the local-name () Function

1:  <?xml version=″“1.0”" encoding="UTF-8"?>
2:  <xsl:stylesheet version=″“1.0”"
3:    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
4:    xmlns:car="http://www.example.com/xmlns/car"
5:    xmlns:man="http://www.example.com/xmlns/manufacturer">
6:
7:    <xsl:output method="text" encoding="UTF-8" />
8:    <xsl:strip-space elements="*" />
9:
10:   <xsl:template match="/">
11:     <xsl:apply-templates />
12:   </xsl:template>
13:
14:   <xsl:template match="car:models">
15:     <xsl:text>Car models:&#xA;</xsl:text>
16:     <xsl:apply-templates select="car:model/@car:name" />
17:     <xsl:text>&#xA;</xsl:text>
18:   </xsl:template>
19:
20:   <xsl:template match="man:manufacturers">
21:     <xsl:text>Manufacturers:&#xA;</xsl:text>
22:     <xsl:apply-templates select="man:manufacturer/@man:name" />

23:   </xsl:template>
24:
25:   <xsl:template match="@*[local-name ()='name']">
26:     <xsl:value-of select="." />
27:     <xsl:text>&#xA;</xsl:text>
28:   </xsl:template>
29: </xsl:stylesheet>

ANALYSIS

Listing 15.19 operates on Listing 15.18, so it needs both namespaces declared in Listing 15.18 declared as well. These declarations appear on lines 4 and 5. Listing 15.19 will create a text document that consists of a list of cars and a list of manufacturers. Both have a name attribute, and the template starting on line 25 will match both of them. In fact, the match expression will match any name attribute in any namespace because it uses the local-name () function and compares the result with a string. You also can use the local-name () function on different nodes by passing a select expression as the argument. The result is shown in Listing 15.20.

OUTPUT

LISTING 15.20 Result from Applying Listing 15.19 to Listing 15.18

Car models:
Golf
Camry
Focus
Civic
Prizm

Manufacturers:
Volkswagen
Toyota
Ford
Chevrolet
Honda

Summary

In today’s lesson, you learned that you can use namespaces to keep apart vocabularies. Namespaces are denoted by a prefix that can be used in front of an element or attribute name. The name and the namespace prefix are separated by a colon. To be able to use a certain prefix, you need to declare it first in or before the outermost element using the prefix. It is a good idea to declare all namespaces in the root of a document rather than scatter namespace declarations throughout a document.

Although namespaces are related to DTDs and Schemas, the namespace declaration is not meant to point to a DTD or Schema to get the vocabulary for a certain namespace. The validation process of a source document and the namespace declarations are separate functionalities.

In cases in which you need to output a namespace that is used as part of XSLT (or another XML language), you can define a namespace alias for that namespace. You can use another prefix so that the nodes will be seen as output, not as XSLT elements to be processed.

In tomorrow’s lesson, you’ll learn more details about selecting data. You’ll revisit and examine further some concepts you learned in the first week, and learn some additional techniques to select data.

Q&A

Q Is there a limit to the number of namespaces I can use in a document and a stylesheet?

A No. You can use any number of namespaces. Your documents will, however, not become more readable if they have many namespaces. You should probably make a different document design.

Q Can I alias the default namespace?

A Yes. You can use #default as a pseudo prefix for the default namespace.

Q Why isn’t there a function that returns just the namespace prefix?

A There is really no need for such a function. You can address elements in the same namespace by using prefix:*. If you really need only the namespace prefix, use substring-before (name (),':').

Workshop

This workshop tests whether you understand all the concepts you learned today. It is helpful to know and understand the answers before starting tomorrow’s lesson. You can find the answers to the quiz questions and exercises in Appendix A.

Quiz

1. True or False: Attributes in an element can have different namespaces.

2. True or False: The namespace prefix in a stylesheet needs to be the same as that of the source document.

3. Why isn’t a namespace name supposed to point to a DTD or an XML Schema?

4. Why doesn’t an expression matching a specific element name also match elements in a namespace with the same name?

5. How can you make sure that a namespace is not declared in the output if that namespace isn’t used in the output?

Exercise

1. The following stylesheet comes from Day 8, “Working with Variables.” It creates an HTML document for an auto show, with the manufacturers listed with their cars. Change the stylesheet so that it works with Listing 15.18. Make sure that the namespace declarations are not carried over to the output.

<?xml version=″“1.0”" encoding="UTF-8"?>
<xsl:stylesheet version=″“1.0”"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="html" version="4.0" />

  <xsl:template match="/">
    <html>
    <body>
      <h1>Auto show</h1>
      <xsl:apply-templates select="/cars/manufacturers" />
    </body>
    </html>
  </xsl:template>

  <xsl:template match="manufacturers">
    <xsl:for-each select="manufacturer">
      <h2><xsl:value-of select="@name" /></h2>
      <p><i>Country: <xsl:value-of select="@country" /></i></p>
      <xsl:variable name="mfc" select="." />
      <xsl:for-each select="/cars/models/model[@manufacturer = $mfc/@id]">
        <ul>
          <li>
            <xsl:value-of select="@name" />
            <xsl:text>  (</xsl:text>
            <xsl:value-of select="@year" />
            <xsl:text>)</xsl:text>
          </li>
        </ul>
       </xsl:for-each>
     </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.22.181.47