Simon Woods
Simon Woods

Reputation: 2241

XPath/XQuery - matching all elements containing a particular value

My XML looks a bit like this:

<root>
  <child1>
    <grandchild1>{tagID1}<grandchild1/>
    <grandchild2>{tag2}<grandchild2/>
  </child1>
  <child2>{newtag1}<child2/>
<root/>

I am looking to retrieve all elements whose text() is like "{*}" i.e. contains a string between 2 curlies, but am not too familiar with xpath.

I thought the xquery syntax would be something like

"//*[matches(., '{*}')]"

but that is failing with unknown method "-->matches(.<--"

I'd be grateful if someone could correct me please.

Upvotes: 2

Views: 2448

Answers (2)

Petr Janeček
Petr Janeček

Reputation: 38434

The matches() function is available to XPath 2.0, but your XPath engine probably only supports XPath 1.0.

If that is the case, you'll have to end up with something like this:

//*[./text()[starts-with(., '{') and substring(., string-length(.), 1)='}']]

It looks for

ANY ELEMENT
//*
   THAT HAS A TEXT NODE
   [./text()                                                               ]
            WHICH STARTS WITH A '{'
            [starts-with(., '{')                                          ]
                                 AND ENDS WITH A '}'
                                 and substring(., string-length(.), 1)='}'

The substring() function call does what you would expect of an ends-with() function if there was any in XPath 1.0.


EDIT (to address OP's comment about starts-with() not being found, either)

I haven't ever seen an XPath engine that doesn't know starts-with(). If it at least recognizes the substring() function, you can try the following workaround:

//*[./text()[substring(., 1, 1)='{' and substring(., string-length(.), 1)='}']]

which should do the same.

Upvotes: 4

BeniBela
BeniBela

Reputation: 16917

Your XPath looks correct, but the xml and regex are wrong.

The {} are special chars and have to be escaped with \. And a * can not be used alone. So the correct expression is (using text() instead of ., so it does not check the text of all descendants) :

//*[matches(text(), '\{.*\}')]

Although that function is only available in XPath 2 (and XQuery, since it is a superset), you could try the full name fn:matches.

In the xml the closing slashes have to be on the other side:

<root>
  <child1>
    <grandchild1>{tagID1}</grandchild1>
    <grandchild2>{tag2}</grandchild2>
  </child1>
  <child2>{newtag1}</child2>
</root>

Upvotes: 3

Related Questions