O XPath sem distinção entre maiúsculas e minúsculas contém () possível?

93

Estou executando todos os textnodes do meu DOM e verifico se o nodeValue contém uma determinada string.

/html/body//text()[contains(.,'test')]

Isso é sensível a maiúsculas e minúsculas. No entanto, também quero pegar Test, TESTou TesT. Isso é possível com XPath (em JavaScript)?

Aron Woost
fonte

Respostas:

110

Isso é para XPath 1.0. Se o seu ambiente for compatível com XPath 2.0, veja aqui .


Sim. Possível, mas não bonito.

/html/body//text()[
  contains(
    translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'),
    'test'
  )
]

Isso funcionaria para cadeias de caracteres de pesquisa onde o alfabeto é conhecido de antemão. Adicione quaisquer caracteres acentuados que você espera ver.


Se puder, marque o texto que lhe interessa com algum outro meio, como encerrá-lo em um <span>que tenha uma determinada classe durante a construção do HTML. Essas coisas são muito mais fáceis de localizar com XPath do que substrings no texto do elemento.

Se isso não for uma opção, você pode deixar JavaScript (ou qualquer outra linguagem de host que você está usando para executar XPath) ajudá-lo a construir uma expressão XPath dinâmica:

function xpathPrepare(xpath, searchString) {
  return xpath.replace("$u", searchString.toUpperCase())
              .replace("$l", searchString.toLowerCase())
              .replace("$s", searchString.toLowerCase());
}

xp = xpathPrepare("//text()[contains(translate(., '$u', '$l'), '$s')]", "Test");
// -> "//text()[contains(translate(., 'TEST', 'test'), 'test')]"

(Dica de chapéu para a resposta de @KirillPolishchuk - é claro que você só precisa traduzir os caracteres que está realmente procurando .)

Essa abordagem funcionaria para qualquer string de pesquisa, sem exigir conhecimento prévio do alfabeto, o que é uma grande vantagem.

Ambos os métodos acima falham quando as strings de pesquisa podem conter aspas simples, caso em que as coisas ficam mais complicadas .

Tomalak
fonte
Obrigado! Além disso, a adição é boa, traduzindo apenas os caracteres necessários. Eu ficaria curioso para saber qual é a vitória por desempenho. Observe que xpathPrepare () pode lidar com caracteres que aparecem mais de uma vez de maneira diferente (por exemplo, você obtém TEEEEEST e teeeeest).
Aron Woost
@AronWoost: Bem, pode haver algum ganho, basta avaliar se você está ansioso para descobrir. translate()em si não importa quantas vezes você repete cada personagem - translate(., 'EE', 'ee')é absolutamente equivalente a translate(., 'E', 'e'). PS: Não se esqueça de votar a favor de @KirillPolishchuk, a ideia foi dele.
Tomalak
2
System.Xml.XmlNodeList x = mydoc.SelectNodes ("// * [contains (translate (text (), 'ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜÉÈÊÀÁÂÒÓÔÙÚÛÇÅÏÕÑŒ', 'abcdefùghghlmênopqrst)';
Stefan Steiger
1
Não. Veja a parte "é claro que você só precisa traduzir os caracteres que está procurando" .
Tomalak
60

Mais bonito:

/html/body//text()[contains(translate(., 'TES', 'tes'), 'test')]
Kirill Polishchuk
fonte
4
+1 Absolutamente. Isso é algo que eu não pensei. (Vou usar isso na minha resposta, é muito melhor do que a rotina JavaScript original que escrevi)
Tomalak
4
não apenas converter TESTpara teste licença Testcomo é?
Muhammad Adeel Zahid
6
@MuhammadAdeelZahid - Não, ele está substituindo "T" por "t", "E" por "e", etc. É uma correspondência de 1 para 1.
Daniel Haley de
Pode ser mais claro fazer translate(., 'TES', 'tes'). Assim as pessoas perceberão que não é uma tradução de palavras, mas sim de letras.
mlissner 01 de
54

Soluções XPath 2.0

  1. Use letras minúsculas () :

    /html/body//text()[contains(lower-case(.),'test')]

  2. Use match () regex correspondente com seu sinalizador que não diferencia maiúsculas de minúsculas:

    /html/body//text()[matches(.,'test', 'i')]

kjhughes
fonte
1
Esta sintaxe não é compatível com Firefox e Chrome? Eu apenas tentei no console e ambos retornaram um erro de sintaxe.
db
1
O Firefox e o Chrome implementam apenas XPath 1.0.
kjhughes
8

Sim. Você pode usar translatepara converter o texto que deseja corresponder em minúsculas da seguinte maneira:

/html/body//text()[contains(translate(., 
                                      'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
                                      'abcdefghijklmnopqrstuvwxyz'),
                   'test')]
Andy
fonte
6

Se você estiver usando XPath 2.0, poderá especificar um agrupamento como o terceiro argumento para contains (). No entanto, os URIs de agrupamento não são padronizados, portanto, os detalhes dependem do produto que você está usando.

Observe que todas as soluções fornecidas anteriormente usando translate () assumem que você está usando apenas o alfabeto inglês de 26 letras.

ATUALIZAÇÃO: XPath 3.1 define um URI de agrupamento padrão para correspondência caseira.

Michael Kay
fonte
3

A maneira como sempre fiz isso foi usando a função "traduzir" no XPath. Não vou dizer que é muito bonito, mas funciona corretamente.

/html/body//text()[contains(translate(.,'abcdefghijklmnopqrstuvwxyz',
                                        'ABCDEFGHIJKLOMNOPQRSTUVWXYZ'),'TEST')]

espero que isto ajude,

Marvin Smit
fonte