200 likes | 339 Views
Using XSLT and XPath to Transform XML Documents that contain Namespaces. Roger L. Costello XML Technologies. Problem. Suppose that the document that we are processing is using namespaces:. <?xml version="1.0"?> <?xml-stylesheet type="text/xsl" href="FitnessCenter.xsl"?>
E N D
Using XSLT and XPath to Transform XML Documents that contain Namespaces Roger L. Costello XML Technologies
Problem Suppose that the document that we are processing is using namespaces: <?xml version="1.0"?> <?xml-stylesheet type="text/xsl" href="FitnessCenter.xsl"?> <FitnessCenter xmlns="http://www.gym.com"> <Member level="platinum"> <Name>Jeff</Name> <Phone type="home">555-1234</Phone> <Phone type="work">555-4321</Phone> <FavoriteColor>lightgrey</FavoriteColor> </Member> </FitnessCenter> Note that we have a default namespace declaration. Thus, FitnessCenter, Member, Name, Phone, and FavoriteColor all belong to the http://www.gym.com namespace.
<?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="html"/> <xsl:template match="/"> <HTML> <BODY> <xsl:apply-templates/> </BODY> </HTML> </xsl:template> <xsl:template match="*"> <xsl:apply-templates/> </xsl:template> <xsl:template match="Member"> Your name is: <xsl:value-of select="Name/text()"/> </xsl:template> <xsl:template match="text()"> <!-- Do nothing --> </xsl:template> </xsl:stylesheet> Output: -- empty -- (see namespaces-example01)
Why is the output empty? <xsl:template match="Member"> Your name is: <xsl:value-of select="Name/text()"/> </xsl:template> This template does not match any element in the instance document! This template matches on a Member element in no namespace. However, in our instance document the Member element is in the http://www.gym.org namespace, i.e., {http://www.gym.com}Member
Namespace Terminology {http://www.gym.com}Member Local name Namespace URI Expanded name = The combination of the namespace URI and the local name
Namespace Terminology (cont.) <gym:FitnessCenter xmlns:gym="http://www.gym.com"> <gym:Member> … </gym:FitnessCenter> <gym:Member> prefix
local-name() • This is a built-in function which returns a string, corresponding to the local name of the element. <xsl:template match="*"> Local name = <xsl:value-of select="local-name(.)"/> <xsl:apply-templates/> </xsl:template> Output: Local name = FitnessCenter Local name = Member Local name = Name Local name = Phone Local name = Phone Local name = FavoriteColor (see namespaces-example02)
<?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="html"/> <xsl:template match="/"> <HTML> <BODY> <xsl:apply-templates/> </BODY> </HTML> </xsl:template> <xsl:template match="*"> <xsl:apply-templates/> </xsl:template> <xsl:template match="*[local-name()='Member']"> Your name is: <xsl:value-of select="*[local-name(.)='Name']/text()"/> </xsl:template> <xsl:template match="text()"> <!-- Do nothing --> </xsl:template> </xsl:stylesheet> Output: Your name is: Jeff (see namespaces-example03)
Alternatively <?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:gym="http://www.gym.com" version="1.0"> <xsl:output method="html"/> <xsl:template match="/"> <HTML> <BODY> <xsl:apply-templates/> </BODY> </HTML> </xsl:template> <xsl:template match="*"> <xsl:apply-templates/> </xsl:template> <xsl:template match="gym:Member"> Your name is: <xsl:value-of select="gym:Name"/> </xsl:template> <xsl:template match="text()"> <!-- Do nothing --> </xsl:template> </xsl:stylesheet> Declare the gym namespace Match on the Member element in the gym namespace Select the Name element in the gym namespace (see namespaces-example04)
namespace-uri() <xsl:template match="*"> Local name = <xsl:value-of select="local-name(.)"/> Namespace URI = <xsl:value-of select="namespace-uri(.)"/> <xsl:apply-templates/> </xsl:template> • This is a built-in function which returns a string corresponding to the namespace URI of the node. Output: Local name = FitnessCenter Namespace URI = http://www.gym.com Local name = Member Namespace URI = http://www.gym.com Local name = Name Namespace URI = http://www.gym.com Local name = Phone Namespace URI = http://www.gym.com ... (see namespaces-example05)
name() Revisited • We have seen the name() function before. It returns the name of the node. But what name does it return if the node is in a namespace? • Answer: it returns the element name and its prefix (this is called the QName, for Qualified Name)
<?xml version="1.0"?> <?xml-stylesheet type="text/xsl" href="FitnessCenter.xsl"?> <gym:FitnessCenter xmlns:gym="http://www.gym.com"> <gym:Member level="platinum"> <gym:Name>Jeff</gym:Name> <gym:Phone type="home">555-1234</gym:Phone> <gym:Phone type="work">555-4321</gym:Phone> <gym:FavoriteColor>lightgrey</gym:FavoriteColor> </gym:Member> </gym:FitnessCenter> <xsl:template match="*"> Local name = <xsl:value-of select="local-name(.)"/> Namespace URI = <xsl:value-of select="namespace-uri(.)"/> Name = <xsl:value-of select="name(.)"/> <xsl:apply-templates/> </xsl:template>
Output: Local name = FitnessCenter Namespace URI = http://www.gym.com Name = gym:FitnessCenter Local name = Member Namespace URI = http://www.gym.com Name = gym:Member Local name = Name Namespace URI = http://www.gym.com Name = gym:Name Local name = Phone Namespace URI = http://www.gym.com Name = gym:Phone Local name = Phone Namespace URI = http://www.gym.com Name = gym:Phone Local name = FavoriteColor Namespace URI = http://www.gym.com Name = gym:FavoriteColor (see namespaces-example06)
Identity transform - copying namespace declarations • Recall our identity transform stylesheet: <xsl:template match="*"> <xsl:element name="{name(.)}"> <xsl:for-each select="@*"> <xsl:attribute name="{name(.)}"> <xsl:value-of select="."/> </xsl:attribute> </xsl:for-each> <xsl:apply-templates/> </xsl:element> </xsl:template> Iterate through each attribute and add them as attributes onto the element.
@* does not select namespace declarations! • The @* will only select non-namespace declaration attributes. It will not select namespace declaration attributes <Library xmlns="http://www.library.org" xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance" id="Boston Public Library"> This will be selected by @* These will not be selected by @*
Identity transformation for XML documents containing namespaces? • So how do we create a stylesheet that can copy over namespace declarations, along with the other attributes? • Answer: use the <xsl:copy/> element
<xsl:copy/> • This element will copy the current element and all namespace declarations to the output file.
<?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml"/> <xsl:template match="* | @*"> <xsl:copy> <xsl:apply-templates select="@*"/> <xsl:apply-templates/> </xsl:copy> </xsl:template> </xsl:stylesheet> (see namespaces-example07) The problem with this identity transform stylesheet is that it's not set up to allow us to make changes to elements/attributes.
<?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml"/> <xsl:template match="*"> <xsl:element name="{name(.)}"> <xsl:copy-of select="namespace::*" /> <xsl:for-each select="@*"> <xsl:attribute name="{name(.)}"> <xsl:value-of select="."/> </xsl:attribute> </xsl:for-each> <xsl:apply-templates/> </xsl:element> </xsl:template> </xsl:stylesheet> Error! Attempting to create an element in a namespace, but the namespace has not been declared yet!
<?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml"/> <xsl:template match="*"> <xsl:element name="{name(.)}" namespace="{namespace-uri(.)}"> <xsl:copy-of select="namespace::*" /> <xsl:for-each select="@*"> <xsl:attribute name="{name(.)}" namespace="{namespace-uri(.)}"> <xsl:value-of select="."/> </xsl:attribute> </xsl:for-each> <xsl:apply-templates/> </xsl:element> </xsl:template> </xsl:stylesheet> Simultaneously declare the element and its namespace Simultaneously declare the attribute and its namespace (see namespaces-example08)