PCkurier 25/2001 >> DLA PRAKTYKÓW >> KURSY
XML w języku Perl
Tam i z powrotem
Autor: Maciej Koziński
Extensible Markup Language (XML) staje się coraz bardziej popularnym narzędziem
reprezentowania danych. Język Perl ma zarówno wbudowane mechanizmy, jak i moduły
rozszerzeń, ułatwiające stosowanie XML. Przedstawiamy niektóre z nich.
Extensible Markup Language
XML jest uproszczoną wersją SGML (Standarized General Markup Language), rozwijaną przez World Wide Web Consortium.
Celem tej specyfikacji jest m.in. rozszerzenie możliwości WWW poprzez dostarczenie elastycznych mechanizmów opisu danych.
XML jest metajęzykiem opisu danych i innych języków (np. Wireless Markup Language stosowany w WAP). Nie stanowi on stałego podzbioru elementów, jak HTML, dzięli czemu daje użytkownikowi możliwość tworzenia nowych formatów dokumentów.
Pozwala na tworzenie własnych struktur danych, niezależnych od platformy programowej oraz definiowanie własnych znaczników. Nie opisuje sposobu prezentacji danych - niezbędne jest stosowanie innych technologii (np. CSS czy związanego z XML eXtended Stylesheet Language - XSL). Elastyczność pozwala zarazem uniknąć problemów pojawiających się w rozwoju HTML - niezgodności poszczególnych elementów w implementacjach narzędzi różnych producentów. Założenia projektowe XML
obejmowały m.in.:
- łatwość stosowania w Internecie,
- możliwość zastosowania w aplikacjach różnego typu,
- zgodność z SGML,
- łatwość obsługi ze strony programisty i tworzenia aplikacji,
- zminimalizowanie opcjonalnych własności języka,
- czytelność dla użytkownika, bez konieczności posiadania wyspecjalizowanych narzędzi,
- łatwość tworzenia dokumentów,
- szybkość rozwoju standardu.
XML może reprezentować dane różnego typu - w odróżnieniu od HTML, nie opisującego danych, a jedynie strukturę i (częściowo) sposób prezentacji dokumentu. Spektrum potencjalnych zastosowań XML jest niezwykle szerokie. Można z jego pomocą opisać np. dane muzyczne, chemiczne, genealogiczne, historyczne, inżynierskie, finansowe, grafikę wektorową, równania matematyczne i wiele innych danych o bogatej, złożonej strukturze. Spore nadzieje wiąże się z zastosowaniem XML do elektronicznej wymiany dokumentów.
O wzroście zainteresowania XML jako użytecznym narzędziem zapisu danych świadczy jego wykorzystanie w najnowszych pakietach biurowych (np. w testowej wersji Open Office 6.0). Niewątpliwie zastosowanie jednolitego, elastycznego i otwartego formatu reprezentacji danych ułatwi ich wymianę między aplikacjami i organizacjami.
Do skorzystania z dobrodziejstw XML niezbędne jest posiadanie odpowiednich narzędzi do konwersji z najczęściej dotychczas używanych formatów. Archiwum CPAN zawiera wiele modułów języka Perl, realizujących konwersję danych do i z XML. Wielość tych narzędzi, prostota ich pozyskania i użytkowania, a przede wszystkim łatwy dostęp do tworzonych w pamięci operacyjnej złożonych struktur danych, odpowiadających zapisom w pliku XML
to niewątpliwe zalety Perla przy współpracy z XML.
Nauka czytania...
Nim zajmiemy się konwersją znanych formatów danych do XML, przyjrzyjmy się przenoszeniu danych pomiędzy XML a strukturami Perla. Budowa odpowiednio złożonych drzew, a zwłaszcza dostęp do poszczególnych elementów są w Perlu proste. Wystarczy wskazać początek drzewa i poszczególne wskaźniki, by dojść do wybranego elementu. Nie wymaga to pisania złożonych, rekurencyjnych procedur wyszukujących, jak np. w języku C - wystarczy umiejętność posługiwania się odpowiednimi operatorami.
Aby odczytać dane z pliku XML i umieścić je w odpowiedniej strukturze, można np. napisać własny analizator składni oraz podprogram zarządzający drzewami danych w pamięci. Byłoby to jednak wyważanie otwartych drzwi, ponieważ jest wiele modułów implementujących to na różne sposoby. Jednym z nich jest XML::Parser - ten moduł
analizatora składni XML jest perlową nakładką na bibliotekę Expat (przeznaczoną dla języka C) i jednym z pierwszych modułów Perla przeznaczonych do przetwarzania XML. Korzystanie z niego jest jednak dużo mniej komfortowe niż z nowszych narzędzi perlowych. Pokażemy więc tylko jeden, ograniczony do minimum przykład jego zastosowania, niezbędny do skorzystania z modułu XML::SimpleObject. Dostęp do danych nie jest wygodny -
trzeba tworzyć specjalne procedury wywoływane w reakcji na zdarzenia (handlers). Poniższy program tworzy nowy obiekt XML::Parser, po czym wywołuje metodę przeprowadzającą analizę składniową wskazanego pliku:
$f = "./znajomi.xml";
$p = XML::Parser->new();
$p->parsefile ($f);
Pozyskanie danych z XML do wewnętrznych struktur Perla jest znacznie przyjemniejsze przy wykorzystaniu modułu XML::Simple. Utworzone w pliku XML drzewiaste struktury danych znajdują odzwierciedlenie w tablicy zawierającej odwołania do kolejnych węzłów drzewa. Taka wewnętrzna reprezentacja pozwala na proste odwoływanie się do poszczególnych elementów poprzez kolejne wskazania. Nie jest konieczne pisanie złożonych procedur do "wędrówki" po węzłach drzewa. Dostęp do kolejnych elementów możemy uzyskać, korzystając na odpowiednim poziomie drzewa z instrukcji wyliczenia:
foreach $k (keys (%{$xml->{znajomi}}))
Przyjrzyjmy się w praktyce strukturom danych tworzonych w wyniku przetworzenia pliku XML za pomocą modułu XML::Simple. Rozpatrzmy plik XML o nazwie znajomi.xml (patrz wydruk 1 ). Dane z tego pliku możemy wczytać do tablic asocjacyjnych Perla w następujący sposób:
use XML::Simple;
my $file = './znajomi.xml';
my $xs1 = XML::Simple->new();
my $doc = $xs1->XMLin($file);
Utworzenie obiektu XML::Simple i skorzystanie z jego metody XMLin(nazwapliku) tworzy w pamięci tablicę zawierającą odwołania do drzew reprezentujących już konkretne elementy pliku XML, do których możemy odwołać się bezpośrednio, używając rozwlekłego zapisu:
print $doc->{osoba}->{nazwisko};
lub
print $doc->{osoba}->{wyglad}->{wzrost};
Całość utworzonej w pamięci struktury danych możemy obejrzeć, korzystając z modułu Data::Dumper: use Data::Dumper;
print Dumper ($doc);
Innym ciekawym sposobem reprezentowania danych XML jest przekształcenie ich do postaci obiektów języka Perl.
Umożliwia to moduł XML::SimpleObject. Do zainicjowania obiektu niezbędny będzie moduł XML::Parser - drzewo skonstruowane w wyniku jego działania zostanie przekształcone w obiekt.
Obiekt powołany do życia przez XML:: SimpleObject zawiera kilka metod udostępniających dalsze gałęzie drzewa lub listy ich nazw. Poszczególne elementy są identyfikowane nazwami (ciągami znaków), podobnie jak w znacznikach XML. Istnieją również metody udostępniające atrybuty elementu lub jego wartość:
• child (nazwa) - odwołanie do obiektu o wymienionej nazwie,
• children (nazwa) - odwołanie do tablicy obiektów elementu wymienionego jako argument; wywołanie tej metody w wyliczeniu foreach zapewnia programowi dostęp do poszczególnych elementów, ich
atrybutów i wartości,
• attribute (nazwa) - zwraca wartość atrybutu danego obiektu o nazwie podanej jako argument,
• value - zwraca wartość wskazanego elementu,
• attributes - zwraca odwołanie do tablicy asocjacyjnej atrybutów wskazanego elementu,
• children_names - zwraca odwołanie do tablicy nazw elementów potomnych wskazanego elementu.
A oto praktyczny przykład użycia modułu XML::SimpleObject do stworzenia obiektowej reprezentacji danych XML.
Rozważmy przykład z plikiem znajomi.xml. Utworzenie obiektu reprezentującego dane z tego pliku oraz mechanizmy dostępu do nich przedstawia wydruk 2 .
Tworzymy nowy obiekt analizatora składni XML::Parser z atrybutem Style o wartości Tree. Opcja ta wyznacza typ danych zwracanych przez analizator składni (poszczególne style i metody ich rozbudowy są opisane w dokumentacji systemowej modułu). Dane z analizatora składni są przekazane nowo tworzonemu obiektowi XML::SimpleObject.
Okazuje się, że łatwiej jest odczytać plik XML i utworzyć z danych struktury w pamięci operacyjnej, niż przeprowadzić operację odwrotną. Korzystając z modułu XML::Simple, możemy także zapisać "drzewiastą"
strukturę danych do pliku XML, jednak z ograniczeniami. Mechanizmy modułu nie umożliwiają wyboru pomiędzy zapisaniem pewnej porcji danych jako elementu XML lub jako atrybutu tegoż elementu - podobne ograniczenia napotkamy przy odczycie plików XML, co zawęża użyteczność tego modułu do prostych danych, nie zawierających atrybutów. Sposób zapisu przy użyciu XML:: Simple demonstruje program na wydruku 3 .
Ograniczenia modułu XML::Simple można oczywiście w prosty sposób obejść, stosując moduł XML::Writer.
Umożliwia on ingerencję w sposób zapisu danych w pliku XML nawet na elementarnym poziomie znaczników XML, ale kosztem łatwości korzystania z mechanizmu zapisu - programista musi sam dokładnie określić, co i w jaki sposób ma zostać zapisane. Pozwala to dokonywać zapisów w sposób dość dowolny - nie musimy bowiem uporządkować tablicy asocjacyjnej z danymi do zapisu dokładnie w taki sposób, w jaki będzie zapisana w pliku XML. Możemy manipulować danymi nawet na etapie zapisywania ich do pliku.
Przykładowy zapis złożonej, wielopoziomowej tablicy asocjacyjnej do pliku XML, przy użyciu modułu XML::Writer przedstawia wydruk 4 .
Na tym wydruku znacznik o nazwie "osoba" zostaje zainicjowany metodą StartTag z atrybutem "nazwisko", wybranym z pola "nazwisko" obecnie przetwarzanego rekordu. Mechanizm ten będzie z pewnością użyteczny przy konwersji dokumentów XML do HTML, gdzie poszczególne znaczniki mają swoje atrybuty przy odwołaniach, dla takiego np. adresu URL:
<a href="http://www.dokumentacja.pl/rozdzia•">Rozdzia• 1</a>
Zapisanie danych w postaci HTML może wyglądać tak, jak na wydruku 5 .
Podobnie wyglądać będzie zapis dla odsyłaczy <IMG>. Moduł XML::Writer będzie niezbędny, jeśli zechcemy zapisać dane w postaci HTML tam, gdzie wystąpią zagnieżdżone elementy mające własne atrybuty, np. odsyłacz
<IMG> znajdzie się wewnątrz odsyłacza <A HREF>.
Dane wyjściowe z XML::Writer kierowane są do pliku standardowego wyjścia. Możemy to zmienić, korzystając z modułu IO::File:
use XML::Writer;
use IO;
my $output = new IO::File(">output.xml");
my $writer = new XML::Writer(OUTPUT => $output);
...
$output->close();
Moduł XML::SimpleObjects nie udostępnia mechanizmów zapisu danych do pliku - w tym celu należy skorzystać z modułów XML::Simple i XML::Writer.
Konwersja na całego
Skoro umiemy już przenosić informacje między strukturami danych Perla a XML, możemy spróbować przenieść do XML dane z popularnych formatów, np. z CSV. Dane tabelaryczne zgromadzone w najprostszym formacie -
kolumn podzielonych separatorem - możemy łatwo przekształcić do postaci XML przy użyciu modułu XML::CSV.
Do poprawnej pracy wymaga on modułu Text::CSV_XS. Jest on odpowiedzialny za właściwy "rozbiór" pliku CSV, natomiast jego funkcjonowanie ukryte jest za interfejsem XML::CSV. Przykład pliku CSV o nazwie znajomi.csv zawiera wydruk 6 .
Tworzymy nowy obiekt ze wskazaniem column_headings na łańcuch zawierający nagłówki kolumn, które w trakcie konwersji zostaną zamienione na nazwy elementów. Za pomocą column_data można też wskazać dwuwymiarową tablicę zawierającą dane. Metoda parse_doc() wczytuje i przetwarza podany jako parametr plik CSV. Możemy też zadeklarować pobranie nazw kolumn (i przyszłych elementów XML) z danego wiersza pliku CSV: my $csv = XML::CSV->new({column_headings =>
$csv->parse_doc("in_file.csv", {headings => 1});
Metoda print_xml wpisze treść XML do pliku o podanej nazwie, z głównym znacznikiem file_tag oraz znacznikiem oddzielającym poszczególne rekordy parent_tag. Wynik działania programu na danych z pliku znajomi.csv
Większość popularnych aplikacji jest w stanie zapisać dane tabelaryczne w formacie CSV. W module XML::CSV
mamy więc narzędzie zdolne przekształcić dane z wielu aplikacji do postaci XML. Komu droga przez CSV wyda się zbyt długa, może skorzystać ze specjalizowanych modułów. Jednym z nich jest XML::Excel, który jest podobny do obiektu XML::CSV - jest inicjowany w ten sam sposób, ma identyczne pola i metody. Sposób korzystania z niego jest taki sam, za wyjątkiem oczywiście źródła danych - musi być to arkusz kalkulacyjny Excela.
Baza do XML
Zajrzyj
- www.w3.org/TR/REC-xml - drugie wydanie specyfikacji XML 1.0
- www.xml.com - XML i technologie pokrewne - podręczniki, informacje, narzędzia
- www.xmlpitstop.com - pomoc przy korzystaniu z technologii XML, informacje, zasoby programowe i odsyłacze
- www.zvon.org - przewodniki i narzędzia dla programistów i webmasterów korzystających z XML
- www.xmlsoftware.com - zbiór informacji i odsyłacze do oprogramowania przetwarzającego XML
- www.ucc.ie/xml - zbiór pytań i odpowiedzi (FAQ) na temat XML
- www.garshol.priv.no/download/xmltools - zbiór odsyłaczy do bezpłatnie udostępnianych narzędzi i oprogramowania związanego z technologią XML
- xmlsoft.org/xml.html - biblioteka XML dla środowiska GNOME
- www.jclark.com/xml - zestaw odsyłaczy do narzędzi przetwarzania XML
- www.jclark.com/xml/expat.html - strona biblioteki języka C Expat - XML Parser Toolkit
- www.spiderpro.com/bu/buxmlm001.html - Kick-start XML - przewodnik po XML dla początkujących
- www.rpbourret.com/xmldbms/index.htm - biblioteka middleware zapewniająca wymianę danych między relacyjnymi bazami danych a XML z poziomu Javy i Perla
- www.perl.com - język Perl - zasoby, artykuły, przewodniki
Najbardziej intrygującym zagadnieniem będzie przeniesienie informacji z systemów zarządzania relacyjnymi bazami danych do XML. Elastyczność zapytań SQL, możliwość reprezentowania ich wyników w formacie XML
dostępnym dla innych aplikacji niewątpliwie kusi programistów. A jest o co powalczyć - jak widać, nie jest to takie trudne, jeśli skorzystamy z modułu DBIx::XML_RDB. Umożliwia on zadanie zapytania w języku SQL do systemu bazodanowego i zapisanie jego wyników w formacie XML, w dodatku czyni to w sposób mało skomplikowany (patrz wydruk 8 ).
Tworzymy obiekt DBIx::XML_RDB, podając jako parametr ciąg identyfikujący odpowiedni sterownik silnika bazodanowego oraz bazę danych i serwer - dla obiektu DBI - a także sterownik, nazwę użytkownika i hasło.
Metoda DoSql wykonuje podane zapytanie SQL, którego treść jest konwertowana do postaci XML i zachowywana w polu GetData. Stamtąd możemy przepisać ją w dowolne miejsce, np. do pliku - jak w naszym przykładzie -
najprostszą funkcją "print". Przy składowaniu w tabeli "ksiazka_adresowa" danych, sformułowanych analogicznie jak w przykładzie z tabelą CSV, zawartość pliku adresy.xml wyglądać będzie jak na wydruku 9 .
Operacja odwrotna nie doczekała się własnego modułu - poniekąd słusznie, ponieważ przekształcenie danych reprezentowanych w XML jako drzewo do postaci tabel relacyjnej bazy danych nie jest zadaniem dającym się łatwo zautomatyzować. Każdy przypadek wymagać będzie zaprojektowania specjalnej bazy, osobnego rozkładu drzewa na poszczególne elementy i przydzielenia ich do poszczególnych tabel. Stąd tylko część tego zadania - a więc stworzenie wewnętrznych struktur danych z pliku XML - można powierzyć opisanym w artykule modułom.
* * *
Przedstawione tu sposoby przenoszenia danych pomiędzy XML a innymi formatami danych pokazują jedynie ułamek możliwości tkwiących w tym metajęzyku. XML ze swoją elastycznością i prostotą świetnie komponuje się z mechanizmami Perla - zwłaszcza z jego łatwością dostępu do złożonych struktur danych w pamięci, a wiele modułów rozszerzeń Perla ułatwia zastosowanie XML w interakcji z różnymi aplikacjami i środowiskami.