6

I'm trying to write an Xpath Statement (1.0) that can read info from a 'search' node and perform a search using it.

I was making some nice progress, but stumbled across an issue where if an attribute (used for a value in the search) is empty or doesn't exist, it fails.

Code Edited to simplify Example:

So Here is my sample XML:

<xml>
    <files>
        <file name="foo" description="" rating="4"/>
        <file name="food" description="" rating="4"/>
        <file name="foobar" description="" rating="3"/>
        <file name="bar" description="" rating="3"/>
        <file name="barter" description="" rating="3"/>
        <file name="barterer" description="" rating="2"/>
    </files>
    <searches>
        <search id="1">
            <exclude>
                <file term="foo"/>
            </exclude>
        </search>
    </searches>
</xml>

And working XPATH:

//files/file[
            not(contains(@name, //search[@id='1']/exclude/file/@term))
]

It works as expected...

However if the an expected attribute is missing or empty it will fail to work. I think because: contains(@attrib, "") matches everything for some-reason, therefore a not() will always match nothing if the attribute is "" or not present.

For Example, if I alter the exclude fragment of XML to this it fails:

            <exclude>
                <file term=""/>
            </exclude>

with this too:

            <exclude></exclude>

Is there a way to Check for an empty value and not perform the select? or is there perhaps a better way of structuring the Logic. Bare in mind I cannot use Conditionals or the other functions in Xpath2.0.

Futile32
  • 834
  • 2
  • 8
  • 15
  • Could you, please, edit the question and describe the rules for finding a search match -- these aren't provided and the long Xpath expression is rather confusing -- I suspect that a shorter expression can be used. For example: `cond1 and cond2 or cond1 or cond2` can simply be substituted by the shorter, equivalent expression `cond1 or cond2` . – Dimitre Novatchev Sep 02 '12 at 16:02
  • Sorry about that. I updated the Code examples to more clearly point to my problem. – Futile32 Sep 02 '12 at 19:58
  • @Futile32, maybe your XPath get the nodes with name="", and because you have none empty, you don't get any. – Chani Poz Oct 15 '12 at 15:06

3 Answers3

2

Why does the Xpath function contains() return everything if it search param is blank or missing?

Because that is what the XPath specification says the contains() function should do:

If the value of $arg2 is the zero-length string, then the function returns true.

You could adjust your XPath and simplify some of the conditions with the following:

//files/file[
            (  
                (
                    not(//search[@id='1']/include/file/@term)
                    or
                    (
                    contains(@name, //search[@id='1']/include/file/@term) 
                    or
                    contains(@description, //search[@id='1']/include/file/@term) 
                    ) 
                )
                or
                contains(@rating, //search[@id='1']/include/file/@rating)   
            )    
            and 
            ( 
                 (
                    not(//search[@id='1']/exclude/file/@term)
                    or
                    (
                    not(contains(@name, //search[@id='1']/exclude/file/@term)) 
                    and
                    not(contains(@description, //search[@id='1']/exclude/file/@term))
                    )
                )
                and
                (
                    not(//search[@id='1']/exclude/file/@rating)
                    or
                    not(contains(@rating, //search[@id='1']/exclude/file/@rating))
                )
            )

            ]
Mads Hansen
  • 63,927
  • 12
  • 112
  • 147
  • Thanks for the reference, however I guess I was hoping to find a way I can use the function differently to solve my problem. I have reworded my question to indicate that. – Futile32 Sep 02 '12 at 20:00
0

Perhaps you want to say something like

//files/file[
   not(contains(@name, 
                //search[@id='1']
                /exclude/file/@term))
   or not(
     normalize-string(//search[@id='1']
                      /exclude/file/@term)
   )
]
C. M. Sperberg-McQueen
  • 24,596
  • 5
  • 38
  • 65
0

So I stumbled across the answer in a different post. Below is an example of it working. Thanks for everyone's advise.

//files/file[
    not(
        contains(
            @name,
            concat( //search[@id='1']/exclude/file/@term,
                substring('??', 1 + 2*
                    boolean( substring( //search[@id='1']/exclude/file/@term, 1 ) )
                )
            )
        )
    )
]

The bit where I placed "??" probably wants replacing with an invalid char or something. For each additional character used the 1+ needs to increment. For me I'm checking filenames so a questionmark seems a good idea. To be honest I probably won't be using this route, I was just eager to solve the problem after all this time.

Here is where I got the idea from: How to give back constant if node does not exist in XPATH?

Community
  • 1
  • 1
Futile32
  • 834
  • 2
  • 8
  • 15