Uroki XML-a

https://xpil.eu/pGtV7

Przytrafiło mi się ostatnio brać udział w niedużym projekcie, w którego centrum siedziały sobie dane w formacie XML.

Zawsze unikałem XML-a jak ognia, ponieważ - jak ogólnie wiadomo - prościej, łatwiej i przyjemniej jest spędzić godzinkę siedząc gołym tyłkiem na rozżarzonych węglach, popijając kwas siarkowy i zagryzając go kaktusami, niż mieć do czynienia z XML. Jednakowoż czasem jest tak, że jak trzeba to trzeba i żadne rozpaczliwe wołania nie pomogą.

Po mniej więcej dwóch dniach zacząłem się w tym XML-owym bajzlu trochę orientować, a po kolejnych trzech byłem skłonny zaryzykować stwierdzenie, że jeszcze trochę i będę wolał XML-a od tych rozżarzonych węgli.

Dziś króciutko na temat wyszukiwania, porównywania oraz wielkości liter. W XML-u, rzecz jasna.

Na dzień dobry zaczniemy od takiego prostego dokumentu XML:

<xml version="1.0">
<ksiazki>
  <ksiazka id="1" autor="Jan Kowalski" tytul="Moja pierwsza książka z tytułem" />
  <ksiazka id="2" autor="Adam Malinowski" tytul="Książka z tytułem innym, niż Jana Kowalskiego" />
  <ksiazka id="3" autor="Adam Malinowski" tytul="Jakaś całkiem nieznana książka" />
  <ksiazka id="4" autor="Joanna Będzielewska-Chrumczyńska" tytul="Podziobać meduzę" />
  <ksiazka id="5" autor="Grzegorz Garlicki" tytul="Ostatni" />
</ksiazki>

Załóżmy teraz, że chcemy przeszukać nasz zbiór książek i znaleźć wszystkie tytuły zawierające słowo "książka".

Najbardziej oczywiste podejście:

[raw] .SelectNodes('//ksiazki/ksiazka[contains(@tytul, "książka")]')
[/raw]

Powyższe "zapytanie" jest zgodne ze składnią XPath. W zależności od tego, jakiego języka używamy, na początku znajdzie się jakaś zmienna oznaczająca nasz dokument XML. Nie będę teraz pisał całego kodu, bo mi się nie chce.

Okazuje się, że powyższe zapytanie znajdzie nam książki o id=1 oraz 3, natomiast pominie książkę z id=2, chociaż jej tytuł zawiera słowo "książka".

Czemu tak?

Otóż operator "contains" jest czuły na wielkość liter i - niestety - nie da się go "odczulić".

Cóż więc począć?

Ano, trzeba dynamicznie zamienić wielkie litery na małe we wszystkich przeszukiwanych tytułach. O, tak:

[raw] .SelectNodes('//ksiazki/ksiazka[contains(translate(@tytul, "AĄBCĆDEĘFGHIJKLŁMNŃOÓPQRSŚTUVWXYZŹŻ", "aąbcćdeęfghijklłmnńoópqrsśtuvwxyzźż"), "książka")]')
[/raw]

Operator translate "tłumaczy" litery w tekście przekazanym w pierwszym argumencie, z liter drugiego argumentu na litery trzeciego argumentu. Tym samym atrybut "@tytul" zostanie najpierw skonwertowany do małych liter, a dopiero potem przeszukany pod kątem wystąpienia słowa "książka". Kod wygląda dość nieelegancko, ale póki co nie znaleziono lepszego rozwiązania...

Oczywiście jeżeli zamiast słowa "książka" szukamy tekstu wprowadzonego przez użytkownika, warto ten tekst również potraktować operatorem translate. Ale to już zupełnie inna historia...

https://xpil.eu/pGtV7

3 komentarze

    1. Nic się nie martw, zazwyczaj po paru godzinach znów zaczynam gadać ludzkim głosem, więc jakoś to będzie…

  1. nie tylko contains jest case sensitive – generalnie cały XML taki jest.
    Prostszym wyjściem wydaje się użycie funkcji xpathowych lower-case lub matches – w tym ostatnim przypadku jest flaga mówiąca o podejściu do wielkości liter

Leave a Comment

Komentarze mile widziane.

Jeżeli chcesz do komentarza wstawić kod, użyj składni:
[code]
tutaj wstaw swój kod
[/code]

Jeżeli zrobisz literówkę lub zmienisz zdanie, możesz edytować komentarz po jego zatwierdzeniu.