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

Subject: Re: [xsl] An XPath expression that avoids writing special casecode?
From: "Roger L Costello costello@xxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Fri, 18 Jun 2021 13:31:15 -0000
Hi Mukul,

I tried your suggestion. For this XML document:

<Document>
    <Row>
        <Cell>
            <Data>aviation</Data>
        </Cell>
        <Cell/>
    </Row>
</Document>

I evaluated the following XPath expression in Oxygen XML:

/Document/Row[Cell[1]/Data eq 'aviation'][Cell[2]/string(Data) eq '']

Oxygen XML returned the desired row:

    <Row>
        <Cell>
            <Data>aviation</Data>
        </Cell>
        <Cell/>
    </Row>

Fantastic!

Question: Below are two XPath expressions. The first is the one that Mukul
recommended and it returns the desired row. The second is the one that I
created and it fails. The only difference between the two XPath expressions is
string(Data) versus Data. Why does the former work whereas the later fails?

/Document/Row[Cell[1]/Data eq 'aviation'][Cell[2]/string(Data) eq '']
/Document/Row[Cell[1]/Data eq 'aviation'][Cell[2]/Data eq '']

/Roger


From: Mukul Gandhi mukulg@xxxxxxxxxxxxxxxxx
<xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Sent: Friday, June 18, 2021 3:28 AM
To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
Subject: [EXT] Re: [xsl] An XPath expression that avoids writing special
casecode?

On Thu, Jun 17, 2021 at 7:36 PM Roger L Costello mailto:costello@xxxxxxxxx
<mailto:xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> wrote:
B 
My XML document contains a bunch of <Row> elements, like so:

<Document>
B  B  ...
B  B  <Row>
B  B  B  B  <Cell>
B  B  B  B  B  B  <Data>airport</Data>
B  B  B  B  </Cell>
B  B  B  B  <Cell>
B  B  B  B  B  B  <Data>airports</Data>
B  B  B  B  </Cell>
B  B  </Row>
B  B  ...
</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">
B  B  <xsl:param name="element"/>
B  B  <xsl:param name="parent"/>
B  B  <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:

B  B  <Row>
B  B  B  B  <Cell>
B  B  B  B  B  B  <Data>aviation</Data>
B  B  B  B  </Cell>
B  B  B  B  <Cell/>
B  B  </Row>

Then this call to f:getRow fails:

<xsl:sequence select="f:getRow('aviation', '')" />B  B  B  B  <!-- 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>
B  B  <Row>
B  B  B  B <Cell>
B  B  B  B  B  <Data>airport</Data>
B  B  B  B </Cell>
B  B  B  B <Cell>
B  B  B  B  B  <Data>airports</Data>
B  B  B  B </Cell>
B  B  </Row>
B  B  <Row>
B  B  B  B <Cell>
B  B  B  B  B  <Data>airport</Data>
B  B  B  B </Cell>
B  B  B  B <Cell>
B  B  B  B  B  <Data>ABC</Data>
B  B  B  B </Cell>
B  B  </Row>
B  B  <Row>
B  B  B  <Cell>
B  B  B  B  B <Data>aviation</Data>
B  B  B  </Cell>
B  B  B  <Cell/>
B  B  </Row>
</Document>B 

XSLT stylesheet,

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

B  B <xsl:output method="xml" indent="yes"/>
B  B 
B  B <xsl:variable name="document" select="/Document"/>

B  B <xsl:template match="/">
B  B  B  <result>
B  B  B  B  B <xsl:copy-of select="f:getRow('airport', 'airports')"/>
B  B  B  B  B <xsl:copy-of select="f:getRow('aviation', '')"/>
B  B  B  </result>
B  B </xsl:template>
B  B 
B  B <xsl:function name="f:getRow">
B  B  B <xsl:param name="element"/>
B  B  B <xsl:param name="parent"/>
B  B  B <xsl:sequence select="$document/Row[Cell[1]/Data eq
$element][Cell[2]/string(Data) eq $parent]" />
B  B </xsl:function>

</xsl:stylesheet>

The output of above XSLT transformation is following,

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



--
Regards,
Mukul Gandhi
http://www.mulberrytech.com/xsl/xsl-list
http://lists.mulberrytech.com/unsub/xsl-list/673357 ()

Current Thread