xsl-list
[Top] [All Lists]

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

2021-06-18 11:23:49
Wendell wrote:

In the first case, the step 'string(Data)', in the absence of a Data node, 
will return '', since string(()) (a string value of nothing) is an empty 
string, 
i.e. a sequence of one member, ('').

In the second case, the failure of the path to reach any node at Data results 
in the comparison () eq '' which should produce a runtime error, since 'eq' 
requires two singleton operands.

Ah!

Fantastic explanation!

Thank you Wendell.

/Roger

From: Wendell Piez wapiez(_at_)wendellpiez(_dot_)com 
<xsl-list-service(_at_)lists(_dot_)mulberrytech(_dot_)com> 
Sent: Friday, June 18, 2021 12:02 PM
To: xsl-list(_at_)lists(_dot_)mulberrytech(_dot_)com
Subject: [EXT] Re: [xsl] An XPath expression that avoids writing special 
casecode?

Roger,

You ask

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 '']

In the first case, the step 'string(Data)', in the absence of a Data node, will 
return '', since string(()) (a string value of nothing) is an empty string, 
i.e. a sequence of one member, ('').

In the second case, the failure of the path to reach any node at Data results 
in the comparison () eq '' which should produce a runtime error, since 'eq' 
requires two singleton operands.

It is useful to keep in mind the distinction between a test for the existence 
of a node, and a test for a node's value. The operators such as '=' have the 
nice feature of letting us collapse these things since it permits many-to-many 
or many-to-none comparisons. The 'eq' operator has the nice feature of not 
letting us collapse these things.

Cheers,
Wendell





On Fri, Jun 18, 2021 at 9:31 AM Roger L Costello 
mailto:costello(_at_)mitre(_dot_)org 
<mailto:xsl-list-service(_at_)lists(_dot_)mulberrytech(_dot_)com> wrote:
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 mailto:mukulg(_at_)softwarebytes(_dot_)org 
<mailto:xsl-list-service(_at_)lists(_dot_)mulberrytech(_dot_)com> 
Sent: Friday, June 18, 2021 3:28 AM
To: mailto:xsl-list(_at_)lists(_dot_)mulberrytech(_dot_)com
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:mailto:costello(_at_)mitre(_dot_)org 
<mailto:mailto:xsl-list-service(_at_)lists(_dot_)mulberrytech(_dot_)com> 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
http://www.mulberrytech.com/xsl/xsl-list 
http://lists.mulberrytech.com/unsub/xsl-list/673357 () 




-- 
...Wendell Piez... ...wendell -at- nist -dot- gov...
...wendellpiez.com... ...pellucidliterature.org... ...pausepress.org...
...http://github.com/wendellpiez... ...gitlab.coko.foundation/wendell... 
http://www.mulberrytech.com/xsl/xsl-list 
http://lists.mulberrytech.com/unsub/xsl-list/673357 () 
--~----------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
EasyUnsubscribe: http://lists.mulberrytech.com/unsub/xsl-list/1167547
or by email: xsl-list-unsub(_at_)lists(_dot_)mulberrytech(_dot_)com
--~--


<Prev in Thread] Current Thread [Next in Thread>