freesoft
freesoft

Reputation: 1144

XPath 2.0: How to implement a map/hash/associative_array

I have a xpath 2.0 expression that implements a map/hash/associative_array:

if ($mykey eq 'key1') then 'val1'  
else if ($mykey eq 'key2') then 'val2'  
else if ($mykey eq 'key3') then 'val3'  
else if ($mykey eq 'key4') then 'val4'  
...
(same for 50 key/value pairs)

Is there a more compact way to do it? (the map is part of a longer xpath expression)

I have tried some tricks by assigning the sequence of pairs to a variable:

for $seq in ('key1', 'val1', 'key2', 'val2' ...) return
    <some trick to access only keys and then the associated value>

but it doesn't assign the sequence to $seq, it loops over each value in the sequence.


Update: Solved: I'm using proposal #1 by Jens Erat: Create an external xml with the mapping. For example, for this xml (cfg.xml):

<cfg>
    <map>
        <key1>val1</key1>
        <key2>val2</key2>
        <key3>val3</key3>
        ...
    </map>
</cfg>

the xpath will be:

doc('cfg.xml')/cfg/map/*[ name() eq $mykey ]

Note: If key1, key2, etc start with a digit, just add a non digit char (for example "k") in the xml, and modify the xpath acordingly:

doc('cfg.xml')/cfg/map/*[ name() eq concat('k',$mykey) ]

Upvotes: 1

Views: 778

Answers (1)

Jens Erat
Jens Erat

Reputation: 38662

Several proposals:

  1. Probably the most reasonable way would be to put the data into some XML document with a key/value pairing and just query this document.
  2. An obvious, but rather hacky-ish solution would be to use string manipulation, eg.

    substring-after(('key1:val1', 'key2:val2')[starts-with(concat($key, ':'))], ':')
    
  3. Using index-of. In XQuery, you would do something like

    let $seq := ('key1', 'val1', 'key2', 'val2')
    let $key := 'key1'
    return $seq[index-of($seq, $key) + 1]
    

    But in XPath 2.0, no let is allowed, and the for hack doesn't work with sequences. You will have to repeat the sequence:

    ('key1', 'val1', 'key2', 'val2')[index-of(('key1', 'val1', 'key2', 'val2'), 'key1') + 1]
    

    But then splitting into two sequences would probably be more reasonable:

    ('val1', 'val2')[index-of(('key1', 'key2'), 'key1')]
    

Upvotes: 1

Related Questions