Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to select elements by part of their name in XSL/XPath?

How do I use apply-templates to select only those elements by name (not value) that end with a specific pattern? Assume the following xml...

<report>
  <report_item>
    <report_created_on/>
    <report_cross_ref/>
    <monthly_adj/>
    <quarterly_adj/>
    <ytd_adj/>
  </report_item>
  <report_item>
   ....
  </report_item>
</report>

I want to use <xsl:apply-templates> on all instances of <report_item> where descendant elements end with 'adj`, so, in this case, only monthly_adj, quaterly_adj, and ytd_adj would be selected and applied with the template.

<xsl:template match="report">
   <xsl:apply-templates select="report_item/(elements ending with 'adj')"/>
</xsl:template>
like image 204
raffian Avatar asked Feb 13 '12 20:02

raffian


People also ask

What does @* mean in XSLT?

The XPath expression @* | node() selects the union, sorted in document order, of (attribute nodes of the context item and XML child nodes of the context item in sense of the XDM).

Which xsl element is used to extract information from XML document?

The XSLT <xsl:value-of> element is used to extract the value of selected node. It puts the value of selected node as per XPath expression, as text.

Does XSLT use XPath?

XPath is a major element in the XSLT standard. XPath can be used to navigate through elements and attributes in an XML document.


2 Answers

I don't think that regular expression syntax is available in this context, even in XSLT 2.0. But you don't need it in this case.

<xsl:apply-templates select="report_item/*[ends-with(name(), 'adj')]"/>

* matches any node

[pred] performs a node test against the selector (in this case *) (where pred is a predicate evaluated in the context of the selected node)

name() returns the element's tag name (should be equivalent to local-name for this purpose).

ends-with() is a built-in XPath string function.

like image 190
harpo Avatar answered Sep 20 '22 00:09

harpo


Slightly neater solution (XSLT 2.0+ only):

<xsl:apply-templates select="report_item/*[matches(name(),'adj$')]"/>

Here is a self-test to prove it works. (Tested on Saxon).

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:fn="http://www.w3.org/2005/xpath-functions" 
                xmlns:xs="http://www.w3.org/2001/XMLSchema" 
                exclude-result-prefixes="xs fn">
 <xsl:output method="xml" indent="yes" encoding="utf-8" />
 <xsl:variable name="report-data">
  <report>
   <report_item>
     <report_created_on/>
     <report_cross_ref/>
     <monthly_adj>Jan</monthly_adj>
     <quarterly_adj>Q1</quarterly_adj>
     <ytd_adj>2012</ytd_adj>
   </report_item>
  </report>
 </xsl:variable>
 <xsl:template match="/" name="main">
  <reports-ending-with-adj>
   <xsl:element name="using-regex">
    <xsl:apply-templates select="$report-data//report_item/*[fn:matches(name(),'adj$')]"/> 
   </xsl:element>
   <xsl:element name="using-ends-with-function">
    <xsl:apply-templates select="$report-data//report_item/*[fn:ends-with(name(), 'adj')]"/>
   </xsl:element>
  </reports-ending-with-adj>
 </xsl:template>
</xsl:stylesheet>
like image 40
Sean B. Durkin Avatar answered Sep 19 '22 00:09

Sean B. Durkin