Re: [xsl] An XPath expression that avoids writing special case code?

Subject: Re: [xsl] An XPath expression that avoids writing special case code?
From: "Mukul Gandhi mukulg@xxxxxxxxxxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Fri, 18 Jun 2021 07:27:52 -0000
On Thu, Jun 17, 2021 at 7:36 PM Roger L Costello costello@xxxxxxxxx <
xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> wrote:


> My XML document contains a bunch of <Row> elements, like so:
>
> <Document>
>     ...
>     <Row>
>         <Cell>
>             <Data>airport</Data>
>         </Cell>
>         <Cell>
>             <Data>airports</Data>
>         </Cell>
>     </Row>
>     ...
> </Document>
>
> I want to fetch the Row whose Cell[1]/Data is 'airport' and whose
> Cell[2]/Data is 'airports'. So I created this XPath expression:
>
> /Document/Row[Cell[1]/Data eq 'airport'][Cell[2]/Data eq 'airports']
>
> I do this kind of fetching operation often, so I created a function to
> fetch the desired Row:
>
> <xsl:function name="f:getRow">
>     <xsl:param name="element"/>
>     <xsl:param name="parent"/>
>     <xsl:sequence select="$document/Row[Cell[1]/Data eq
> $element][Cell[2]/Data eq $parent]" />
> </xsl:function>
>
> I call the function this way:
>
> <xsl:sequence select="f:getRow('airport', 'airports')" />
>
> Sometimes there is an element that doesn't have a parent. That is,
> sometimes I'd like to fetch a Row in which Cell[2] is empty, like this:
>
>     <Row>
>         <Cell>
>             <Data>aviation</Data>
>         </Cell>
>         <Cell/>
>     </Row>
>
> Then this call to f:getRow fails:
>
> <xsl:sequence select="f:getRow('aviation', '')" />        <!-- Those are
> two apostrophes within the parentheses -->
>
> Clearly I need to modify f:getRow. I could add special case code to test
> $parent to see if it is empty (the '' string) and do one thing, and if it's
> not empty do another thing. But I wonder if there is a more elegant
> solution that doesn't involve special case code? Is there a way to modify
> the XPath expression in f:getRow such that it fetches the correct Row
> regardless of whether $parent is empty or not?
>

Testing with XSLT 2.0. Below is an example, for the solution you may be
looking for,

XML input document,

<Document>
    <Row>
       <Cell>
          <Data>airport</Data>
       </Cell>
       <Cell>
          <Data>airports</Data>
       </Cell>
    </Row>
    <Row>
       <Cell>
          <Data>airport</Data>
       </Cell>
       <Cell>
          <Data>ABC</Data>
       </Cell>
    </Row>
    <Row>
      <Cell>
         <Data>aviation</Data>
      </Cell>
      <Cell/>
    </Row>
</Document>

XSLT stylesheet,

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
                         xmlns:f="http://test_fn";
                         exclude-result-prefixes="f"
                         version="2.0">

   <xsl:output method="xml" indent="yes"/>

   <xsl:variable name="document" select="/Document"/>

   <xsl:template match="/">
      <result>
         <xsl:copy-of select="f:getRow('airport', 'airports')"/>
         <xsl:copy-of select="f:getRow('aviation', '')"/>
      </result>
   </xsl:template>

   <xsl:function name="f:getRow">
     <xsl:param name="element"/>
     <xsl:param name="parent"/>
     <xsl:sequence select="$document/Row[Cell[1]/Data eq
$element][Cell[2]/string(Data) eq $parent]" />
   </xsl:function>

</xsl:stylesheet>

The output of above XSLT transformation is following,

<?xml version="1.0" encoding="UTF-8"?>
<result>
   <Row>
       <Cell>
          <Data>airport</Data>
       </Cell>
       <Cell>
          <Data>airports</Data>
       </Cell>
    </Row>
   <Row>
      <Cell>
         <Data>aviation</Data>
      </Cell>
      <Cell/>
    </Row>
</result>


-- 
Regards,
Mukul Gandhi

Current Thread