92

For example, for the XML below

<CATALOG>
    <CD title="Empire Burlesque"/>
    <CD title="empire burlesque"/>
    <CD title="EMPIRE BURLESQUE"/>
    <CD title="EmPiRe BuRLeSQuE"/>
    <CD title="Others"/>
<CATALOG>

How to match the first 4 records with xpath like //CD[@title='empire burlesque']. Is there xpath function to do this? Other solutions like PHP function are also accepted.

Syscall
  • 19,327
  • 10
  • 37
  • 52
Ethan
  • 18,584
  • 15
  • 51
  • 72
  • 1
    http://stackoverflow.com/questions/586231/how-can-i-convert-a-string-to-upper-or-lower-case-with-xslt check out ben gripka's post for xpath 1.0 – user2237201 Apr 02 '13 at 17:36
  • Here, I also have found 2 more solutions with description: (not my site/promotion) https://codingexplained.com/coding/php/solving-xpath-case-sensitivity-with-php – bharat May 06 '19 at 17:09

6 Answers6

130

XPath 2 has a lower-case (and upper-case) string function. That's not quite the same as case-insensitive, but hopefully it will be close enough:

//CD[lower-case(@title)='empire burlesque']

If you are using XPath 1, there is a hack using translate.

Community
  • 1
  • 1
Matthew Flaschen
  • 278,309
  • 50
  • 514
  • 539
66

matches() is an XPATH 2.0 function that allows for case-insensitive regex matching.

One of the flags is i for case-insensitive matching.

The following XPATH using the matches() function with the case-insensitive flag:

//CD[matches(@title,'empire burlesque','i')]
Mads Hansen
  • 63,927
  • 12
  • 112
  • 147
  • 16
    This is regex-based which is an important difference to text-based matching. – usr Jan 20 '14 at 15:58
  • 1
    Beware that this may also find partial matches; if that's not acceptable then use `^` and `$`, like `matches(@title, '^empire burlesque$', 'i')` – Arjan Jun 14 '21 at 19:30
16

This does not work in Chrome Developer tools to locate a element, i am looking to locate the 'Submit' button in the screen

//input[matches(@value,'submit','i')]

However, using 'translate' to replace all caps to small works as below

//input[translate(@value,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz') = 'submit']

Update: I just found the reason why 'matches' doesnt work. I am using Chrome with xpath 1.0 which wont understand the syntax 'matches'. It should be xpath 2.0

Bhuvanesh Mani
  • 1,394
  • 14
  • 23
  • It seems the translate concept is called 'case insensitive collation' in regards to internationalization – neaumusic Mar 25 '20 at 23:46
  • Also bear in mind that the `translate` solution in this answer won't work for languages (german for instance) having special rules for uppercase letters. – Stephan Feb 20 '21 at 07:45
10

One possible PHP solution:

// load XML to SimpleXML
$x = simplexml_load_string($xmlstr);

// index it by title once
$index = array();
foreach ($x->CD as &$cd) {
  $title = strtolower((string)$cd['title']); 
  if (!array_key_exists($title, $index)) $index[$title] = array();
  $index[$title][] = &$cd;
}

// query the index 
$result = $index[strtolower("EMPIRE BURLESQUE")];
Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • 13
    To the anonymous down-voter: Read the OP's question. Especially the *"Other solutions like PHP function are also accepted."* part. – Tomalak Aug 02 '11 at 12:32
8

for selenium xpath lower-case will not work ... Translate will help Case 1 :

  1. using Attribute //*[translate(@id,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='login_field']
  2. Using any attribute //[translate(@,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='login_field']

Case 2 : (with contains) //[contains(translate(@id,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz'),'login_field')]

case 3 : for Text property //*[contains(translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'),'username')]

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
Pravesh Jha
  • 81
  • 1
  • 2
3

You mentioned that PHP solutions were acceptable, and PHP does offer a way to accomplish this even though it only supports XPath v1.0. You can extend the XPath support to allow PHP function calls.

$xpathObj = new DOMXPath($docObj);
$xpathObj->registerNamespace('php','http://php.net/xpath'); // (required)
$xpathObj->registerPhpFunctions("strtolower"); // (leave empty to allow *any* PHP function)
$xpathObj->query('//CD[php:functionString("strtolower",@title) = "empire burlesque"]');

See the PHP registerPhpFunctions documentation for more examples. It basically demonstrates that "php:function" is for boolean evaluation and "php:functionString" is for string evaluation.

jimp
  • 16,999
  • 3
  • 27
  • 36