50

I am trying to build a complex xpath expression which will answer the following condition.

From the XML data below, returns the User entity which:

  1. His loginname is "user1"
  2. His name is "User 1"
  3. He has 2 different profiles values which are "operator" and "admin" (I don't know the exact order ahead)

    <user>
      <login>user1</login>
      <name>User 1</name>
      <profile>
        <value>admin</value>
        <id>2</id>
        <description>admin users</description>
      </profile>
      <profile>
        <value>operator</value>  
        <id>1</id>
        <description>Operator</description>
      </profile>
    </user>
    
    <user>
      <login>user2</login>
      <name>User 2</name>
      <profile>
        <value>admin</value>
        <id>4</id>
        <description>admins users</description>
      </profile>
      <profile>
        <value>poweruser</value>  
        <id>5</id>
        <description>power users</description>
      </profile>
    </user>
    
    </root>
    

Can someone please supply an example for such a case?

EDIT: Added a complex profile entity

Mike
  • 397
  • 2
  • 16
user41767
  • 1,217
  • 1
  • 17
  • 26

4 Answers4

78

The following should do what you're after:

/root/user[login='user1' and 
           name='User 1' and 
           profile='admin' and
           profile='operator']

Having two tests for the profile value might seem odd, but as there are multiple profile nodes then the condition will be satisfied as long as at least one node matches the test.

The reason you can compare profile directly to a string, even though it actually is a node is that the string-value of an element node is the string-value of all its descendants concatenated together, which in this case is just the contents of value.

If profile contained more elements than value you'd have to use a slightly more complex predicate test to determine the existence of a matching profile node based just on the value (this should work with your updated question):

/root/user[login='user1' and 
           name='User 1' and 
           profile[value='admin'] and
           profile[value='operator']]
Greg Beech
  • 133,383
  • 43
  • 204
  • 250
  • 3
    Thanks for your quick solution. May I use an XPATH like this one, or it may change the logic: /root/user[login='user1' ][ name='User 1' ][ profile[value='admin'] ][profile[value='operator']] – user41767 Feb 20 '09 at 09:07
  • 3
    Interesting - I didn't know you could have multiple predicate blocks, but yes, it looks like that works too. Cool, now I have learned something as well :-) – Greg Beech Feb 20 '09 at 09:14
  • Multiple predicates only function as AND of course, the expanded syntax gives you OR too, but I prefer multiples for clarity. You could also do this with axes like preceding-sibling but that would be a bit obtuse for a simple case like this. – annakata Feb 20 '09 at 09:28
  • @Greg Beech: But you could check for duplicates this way, for example "//user[name = preceding-sibling::user/name]". Predicates can be as deeply nested as you wish. – Tomalak Feb 20 '09 at 09:36
  • 1
    The first expression seems outdated (probably the OP changed the XML after the initial solution was posted, by adding the "value" elements). The second expression is OK, but it does not check that the "user" element has *exactly* two "profile" children. – Dimitre Novatchev Feb 20 '09 at 14:46
  • @Dimitre - The question didn't ask for exactly two profile elements (or I didn't read it as such) but a simple "and count(profile)=2" could be added if that was required. – Greg Beech Feb 22 '09 at 22:58
8

Here is a more exact answer (at present Greg Beech's answer does not check for condition 3. in the problem: the user element must have exactly 2 profile children):

/*/user
        [login='user1' 
        and            
         name='User 1' 
        and  
         not(profile[3])
        and          
         profile/value='admin' 
        and           
         profile/value='operator'
         ]
Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
  • Thanks for showing possibility how to check exact number of elements. That said, I still don't read condition 3 as "must have exactly 2 and (not 3 or more)". – Alois Mahdal May 20 '13 at 08:47
  • @AloisMahdal, The question states: "3.He has 2 different profiles values which are "operator" and "admin"". The XPath expression in this answer checks exactly this -- he has the profiles of 'admin' and of 'operator' and he hasn't a third profile. If there was a third profile value, the user would actually have 3 different profile values -- not 2. – Dimitre Novatchev May 20 '13 at 14:32
  • 1
    A job description says that I need to speak French and German. If I speak French, German and Italian, do I qualify? This is about English: I'm not a native speaker and not a linguist so I might be wrong, but way I read it, I assume that unless explicitly said (say, if they really need that you must not speak Italian), such lists are never exclusive. (Note that I'm not intended to prove you wrong, I was just pointing out a "grey zone" in the language. Sorry for semi-OT) – Alois Mahdal May 20 '13 at 15:02
  • @AloisMahdal, I completely agree that natural language has its ambiguities. This is why in many cases we are trying to guess the real meaning of a questions. Not always our guesses are correct and we only know for sure, if the OP has clarified -- which isn't the case with this question. – Dimitre Novatchev May 20 '13 at 15:58
1

Assuming users is the root:

/users/user[login='user1' and name='User 1' 
            and (profile='admin' and profile='operator')]
dirkgently
  • 108,024
  • 16
  • 131
  • 187
0
/root/user[login='user1' and name='User 1' and profile/value='admin' and profile/value='operator'
Nakilon
  • 34,866
  • 14
  • 107
  • 142