As part of my ongoing dabbling with XML/XSLT & XPath in my VB.NET project I’ve decided to document some of the XSLT syntax I’ve found to be useful. In this particular article I will take a look at using templates to navigate the XML structure.
Nomenclature: Definition of Nodes, Elements, and Attributes in an XML Document
XML document structure nomenclature follows the convention node->element->attribute, where node is the base class for elements and attributes. Elements are surrounded by start and end tags with content in between, whereas attributes are name/value pairs contained within elements. For example:
<Element AttributeName="AttributeValue">ElementValue</Element>
Test File Setup
In order to run the template tests I have created three files. The main file is an ASP.NET Web page with an XML Control that calls an XSLT StyleSheet file and an XML Source File. I am including the ASP.NET and XML code below for clarity, as well as the XSLT sample transform files in the examples that follow:
ASP.NET Web Page Code
<%@ Page Language="VB" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> <asp:Xml ID="Xml1" runat="server" DocumentSource="~/XMLFileTest.xml" TransformSource="XSLTFileTest.xslt"></asp:Xml> </div> </form> </body> </html>
XML Source File
<?xml version="1.0" encoding="utf-8" ?> <RootElement> <ElementNode> <ElementToSearchFor AttrName="test" AltAttrName="alternate test"> Some test Text <ASubElement> Sub Element Text </ASubElement> </ElementToSearchFor> <ElementToSearchFor AttrName="Do Not Search" > Second Test Text </ElementToSearchFor> </ElementNode> </RootElement>
Template Match Pattern Examples
The basic match pattern syntax is used to compare a pattern against XML elements. For XSL Templates the match pattern is the same as that of other XSL patterns such as for-each (as well as apply-templates and value-of)
When checking if a particular element with sub-element exists, create a new XSL template and call the template surrounded by an XSL:if test along the following lines:
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl" > <xsl:template match="RootElement"> Start of Main Template<br /> <xsl:if test="ElementNode/ElementToSearchFor"> <xsl:apply-templates select="ElementNode/ElementToSearchFor"/> </xsl:if> End of Main Template<br /> </xsl:template> <xsl:template match="ElementNode/ElementToSearchFor"> Start of Sub Template<br /> End of Sub Template<br /> </xsl:template> </xsl:stylesheet>
If you want to select a particular element based on the element containing a particular attribute within it, you can use the @AttrName syntax as follows:
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl" > <xsl:template match="RootElement"> Start of Main Template<br /> <xsl:if test="ElementNode/ElementToSearchFor[@AttrName]"> <xsl:apply-templates select="ElementNode/ElementToSearchFor[@AttrName]"/> </xsl:if> End of Main Template<br /> </xsl:template> <xsl:template match="ElementNode/ElementToSearchFor[@AttrName]"> Start of Sub Template<br /> End of Sub Template<br /> </xsl:template> </xsl:stylesheet>
To take this one step further, you can use the words and and or to search for the existence of many attributes in the element or of one or the other attributes. Here is an example of looking for element containing two attributes:
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl" > <xsl:template match="RootElement"> Start of Main Template<br /> <xsl:if test="ElementNode/ElementToSearchFor[@AttrName and @AltAttrName]"> <xsl:apply-templates select="ElementNode/ElementToSearchFor[@AttrName and @AltAttrName]"/> </xsl:if> End of Main Template<br /> </xsl:template> <xsl:template match="ElementNode/ElementToSearchFor[@AttrName and @AltAttrName]"> Start of Sub Template<br /> End of Sub Template<br /> </xsl:template> </xsl:stylesheet>
Now if you want to select element s that have an attribute containing a specific text value such as the word test, then you can use the following syntax:
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl" > <xsl:template match="RootElement"> Start of Main Template<br /> <xsl:if test="ElementNode/ElementToSearchFor[@AttrName='test']"> <xsl:apply-templates select="ElementNode/ElementToSearchFor[@AttrName='test']"/> </xsl:if> End of Main Template<br /> </xsl:template> <xsl:template match="ElementNode/ElementToSearchFor[@AttrName='test']"> Start of Sub Template<br /> End of Sub Template<br /> </xsl:template> </xsl:stylesheet>
Interestingly enough, you can also use the // syntax between elements to search for elements that are contained within parent elements. This means you do not have to specify the full path to reach the element that you are interested in. In the case of the sample XML we are using if we want to select the element ASubElement nested within RootElement->ElementNode->ElementToSearchFor we could simply specify a path along the lines of ElementNode//ASubElement. This is definitely pretty useful, here is an example of what the XSL would look like:
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"> <xsl:template match="RootElement"> Start of Main Template<br /> <xsl:if test="ElementNode//ASubElement"> <xsl:apply-templates select="ElementNode//ASubElement"/> </xsl:if> End of Main Template<br /> </xsl:template> <xsl:template match="ElementNode//ASubElement"> Start of Sub Template<br /> End of Sub Template<br /> </xsl:template> </xsl:stylesheet>
It is also possible to select an element based on the text it contains. To do so one can use the contains(element,text) syntax as follows:
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"> <xsl:template match="RootElement"> Start of Main Template<br /> <xsl:if test="ElementNode/ElementToSearchFor[contains(ASubElement,'Sub Element Text')]"> <xsl:apply-templates select="ElementNode/ElementToSearchFor[contains(ASubElement,'Sub Element Text')]"/> </xsl:if> End of Main Template<br /> </xsl:template> <xsl:template match="ElementNode/ElementToSearchFor[contains(ASubElement,'Sub Element Text')]"> Start of Sub Template<br /> End of Sub Template<br /> </xsl:template> </xsl:stylesheet>
Finally, it’s possible to mix and match the above XPath queries as you wish. For example to find the child element ASubElement from the two elements deeper parent element ElementNode where the value of the AttrName Attribute is test, one can put together the following XPath query:
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"> <xsl:template match="RootElement"> Start of Main Template<br /> <xsl:if test="ElementNode//ElementToSearchFor[@AttrName='test']/ASubElement"> <xsl:apply-templates select="ElementNode//ElementToSearchFor[@AttrName='test']/ASubElement"/> </xsl:if> End of Main Template<br /> </xsl:template> <xsl:template match="ElementNode//ElementToSearchFor[@AttrName='test']/ASubElement"> Start of Sub Template<br /> End of Sub Template<br /> </xsl:template> </xsl:stylesheet>
Resources:
Here are two very good resources covering the basics of using XSL Template XPath queries:
Just great. For me XSL is still an area I have to learn. These relatively small chunks of explanation are perfect.
Thanks for your feedback! So far I like the capabilities of XML/XSL quite a bit. I also find the small explanation chunks best for reference purposes and even for remembering. I’m hoping to expand on the XSL explanations into a series of articles as I continue to investigate the language.
I’m looking forward to reading them :)