6.1 The copy Element

You'll be working with several XML documents relating to the European Union (EU) in the examples that follow. You can read more about the EU in at least 12 different languages at http://europa.eu.int/. The document eu.xml, Example 6-1, found in the directory examples/ch06, represents member states, founding member states, and candidate member states from the EU.

Example 6-1. An XML document listing EU member and candidate states
<?xml version="1.0" encoding="UTF-8"?>     <!-- European Union member states and candidate states -->     <eu>  <member>   <state>Austria</state>   <state founding="yes">Belgium</state>   <state>Denmark</state>   <state>Finland</state>   <state founding="yes">France</state>   <state founding="yes">Germany</state>   <state>Greece</state>   <state>Ireland</state>   <state founding="yes">Italy</state>   <state founding="yes">Luxembourg</state>   <state founding="yes">The Netherlands</state>   <state>Portugal</state>   <state>Spain</state>   <state>Sweden</state>   <state>United Kingdom</state>  </member>  <candidate>   <state>Bulgaria</state>   <state>Cyprus</state>   <state>Czech Republic</state>   <state>Estonia</state>   <state>Hungary</state>   <state>Latvia</state>   <state>Lithuania</state>   <state>Malta</state>   <state>Poland</state>   <state>Romania</state>   <state>Slovenia</state>   <state>Slovakia</state>   <state>Turkey</state>  </candidate> </eu>

There are currently, as of early 2003, 15 member states in the EU, and 13 candidate member states. If you wanted to duplicate element nodes from this document, you could use the XSLT copy instruction element, though it has certain limitations.

First, consider this inadequate stylesheet, copy.xsl:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/>      <xsl:template match="eu">   <xsl:apply-templates select="member"/>  </xsl:template>      <xsl:template match="member">    <xsl:apply-templates select="state[2]"/>  </xsl:template>      <xsl:template match="state">   <xsl:copy/>  </xsl:template>     </xsl:stylesheet>

The output method is explicitly XML because that's really the only output method that makes any sense with copy. This stylesheet targets the second state element in eu.xml for copying. This state element has the word Belgium as content, and also has a founding attribute, indicating that Belgium is one of the founding members of the EU. If you apply the stylesheet to eu.xml, however, with:

xalan eu.xml copy.xsl

you get this result:

<?xml version="1.0" encoding="UTF-8"?> <state/>

Where's the attribute value and the element content? Surprise! The copy element doesn't get it for you. It copies the current node, but only if it is an element and has any namespace nodes associated with that element. That's it. This is sometimes called a shallow copy. A shallow copy does not copy any attribute nodes or child nodes, including text nodes. This is a good thing, though, because sometimes that's exactly what you want. I'll show you why.

Suppose, for example, that you wanted to pull all the elements that represent the six founding states of the EU out of eu.xml. You could do this with the stylesheet founding.xsl:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/>      <xsl:template match="eu">   <xsl:apply-templates select="member"/>  </xsl:template>      <xsl:template match="member">  <eu-members>    <xsl:apply-templates select="state[@founding]"/>  </eu-members>  </xsl:template>      <xsl:template match="state">   <xsl:copy>    <xsl:apply-templates/>   </xsl:copy>  </xsl:template>     </xsl:stylesheet>

This stylesheet finds all the state elements that are children of member, and that also have founding attributes, and wraps them all in an eu-members document element. The addition of the apply-templates element as a child of copy means that the template will process all children of the matched nodes, including text, invoking the built-in templates whenever there is a match. When you process eu.xml against founding.xsl:

xalan -i 1 eu.xml founding.xsl

you will get the following:

<?xml version="1.0" encoding="UTF-8"?> <eu-members>  <state>Belgium</state>  <state>France</state>  <state>Germany</state>  <state>Italy</state>  <state>Luxembourg</state>  <state>The Netherlands</state> </eu-members>

The copy element outputs the state element, and apply-templates picks up the text content of the state elements through the built-in template for text. Because attributes are not considered children of elements, apply-templates does not return their values. Attributes have parents, however, which are always elements.

6.1.1 Adding Attributes with copy

The copy element has one optional attribute, use-attribute-sets. In Chapter 2, you saw this attribute in action. It allows you to invoke a named set of attributes stored in an attribute-set element, adding them to an element in the result tree. The following stylesheet, notfounding.xsl, shown in Example 6-2, shows you how it's done.

Example 6-2. An XSLT stylesheet that adds attributes in the course of a copy
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/>      <xsl:attribute-set name="new">   <xsl:attribute name="founding">no</xsl:attribute>  </xsl:attribute-set>      <xsl:template match="eu">   <xsl:apply-templates select="member"/>  </xsl:template>      <xsl:template match="member">  <eu>   <members>    <xsl:apply-templates select="state[not(@founding)]"/>   </members>  </eu>  </xsl:template>      <xsl:template match="state">   <xsl:copy use-attribute-sets="new">    <xsl:apply-templates/>   </xsl:copy>  </xsl:template>     </xsl:stylesheet>

In contrast to founding.xsl, this stylesheet finds state elements that do not have founding attributes by using the XPath Boolean function not( ). When it finds these elements, it uses the attribute set named new to add a new founding attribute to the resulting element. Apply it with:

xalan -i 1 eu.xml notfounding.xsl

and you'll get this result:

<?xml version="1.0" encoding="UTF-8"?> <eu>  <members>   <state founding="no">Austria</state>   <state founding="no">Denmark</state>   <state founding="no">Finland</state>   <state founding="no">Greece</state>   <state founding="no">Ireland</state>   <state founding="no">Portugal</state>   <state founding="no">Spain</state>   <state founding="no">Sweden</state>   <state founding="no">United Kingdom</state>  </members> </eu>

As you can see, the XSLT processor grabbed all state elements that did not have a founding attribute and then added a new founding attribute with a value of no to each of them. The stylesheet also duplicated the eu and members elements using literal result elements.

One other thing: the copy element can contain a template. That's why it can have an apply-templates child, as shown in the following example, identity.xsl. A copy element can even contain other copy elements.

6.1.2 The Identity Transform

You can create an identity transform that copies nodes by using the copy element. The stylesheet identity.xsl matches all nodes and then copies each of them:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes" encoding="US-ASCII"/>     <xsl:template match="@*|node(  )">  <xsl:copy>   <xsl:apply-templates select="@*|node(  )"/>  </xsl:copy> </xsl:template>     </xsl:stylesheet>

The XPath syntax @* matches attributes, or (|) node( ) matches all other nodes. apply-templates then triggers the built-in templates for any node it finds.

The document identity.xml contains an example of each of the seven node types in the XPath data model (root, element, attribute, text, comment, processing instruction, and namespace):

<?xml-stylesheet href="identity.xsl" type="text/xsl"?>     <!-- EU state: Belgium -->     <state member="true" xmlns="urn:wyeast-net:eu">Belgium</state>

Because identity.xml contains an XML stylesheet PI, you can transform it using:

xalan -a identity.xml

The copy looks very much like the original, except Xalan adds an XML declaration:

<?xml version="1.0" encoding="US-ASCII"?> <?xml-stylesheet href="identity.xsl" type="text/xsl"?>     <!-- EU state: Belgium --> <state xmlns="urn:wyeast-net:eu" member="true">Belgium</state>

The copy element works fine in some situations, but in other instances, you may prefer to copy an element's attributes and children automatically, as a matter of course. You can do that with copy-of.



Learning XSLT
Learning XSLT
ISBN: 0596003277
EAN: 2147483647
Year: 2003
Pages: 164

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net