XML – ćwiczenia 13: SAX

Wszystkie przykładowe programy:

SAX – Simple API for XML

SAX jest to interfejs programistyczny oparty o zdarzeniowy model dokumentu. Może być zaimplementowany w obiektowych językach programowania, a interfejsy zbliżone do SAX można spotkać także w językach pozwalających na przekazywanie referencji do funkcji (np. C). My zajmiemy się standardową implementacją w Javie.

Cechy charakterystyczne SAX:

Referencje

API Javy:

Zobacz także:

Parsowanie dokumentu i odczyt danych

Przykład 1.

Pliki: SaxSimplePrinter, InfoHandler.

Program parsuje podany dokument i wypisuje na wyjście informacje o węzłach (niektórych rodzajów). Klasa InfoHandler jest realizacją interfejsu ContentHandler, jej obiekty obsługują zdarzenia SAX.

Przykład 2. LiczbySAX – z wykładu

Plik: LiczbySAX.

Program liczy sumę wartości tych elementów l, które znajdują się w elementach grupa o atrybucie wazne równym tak.

Programista podaje swój kod tworząc klasę implementującą interfejs ContentHandler. Parserowi wskazuje się obiekt, który ma obsługiwać zdarzenia, a podczas parsowania metody tego obiektu wywoływane są w odpowiedzi na zdarzenia takie jak początek elementu czy węzeł tekstowy.

Więcej zdarzeń można obsługiwać, jeśli zaimplementuje się także interfejsy ErrorHandler (obsługa błędów, m.in. walidacji), LexicalHandler (obsługa encji, sekcji CDATA, komentarzy), DeclHandler (obsługa DTD). Szczegóły w API.

Zadanie 1.

Napisz klasę PrintHandler (rozszerzenie DefaultHandler), której działanie polega na wypisywaniu na wyjście dokumentu w postaci sformatowanej (większe wcięcia na kolejnych poziomach zagnieżdżenia elementów).

Wystarczy wypisywać elementy z atrybutami i węzły tekstowe, nie należy przejmować się znakami specjalnymi.

Użyj tej klasy w programie parsującym w trybie SAX i wypisującym w opisany wyżej sposób dowolny dokument.

Podmiana ContentHandlera w czasie parsowania

Przykład 3. Podmiana ContentHandlera

Plik: SaxPodmiana.

Program pokazuje, że można podmienić obiekt ContentHandler-a w trakcie parsowania, a dokładnie w trakcie obsługi zdarzenia przez poprzedniego ContentHandler-a. Kolejne zdarzenia będą obsługiwane przez nowy obiekt.

Obsługa przestrzeni nazw

Podobnie jak w DOM, w SAX możemy przetwarzać dokument uwzględniając lub nie przestrzenie nazw.

Jeśli nie chcemy uwzględniać przestrzeni nazw, powinniśmy:

  • w fabryce ustawić setNamespaceAware(false) (tak na wszelki wypadek, to jest domyślna wartość),
  • parsować dokument parserem uzyskanym z takiej fabryki,
  • odczytywać nazwy z parametru qName w metodzie startElement i metodą getQName dla atrybutów.

Jeśli chcemy uwzględniać przestrzenie nazw, powinniśmy:

  • w fabryce ustawić setNamespaceAware(true),
  • parsować dokument parserem uzyskanym z takiej fabryki,
  • odczytywać nazwy z parametrów uri i localName w metodzie startElement oraz metodami getURI i getLocalName dla atrybutów. W pewnych sytuacjach użyteczne mogą być też nazwy kwalifikowane (z ewentualnym prefiksem), ale to para (uri, localName) decyduje o znaczeniu elementu bądź atrybutu.

Filtry SAX

Filtr to obiekt klasy implementującej interfejs XMLFilter (można rozszerzać standardową klasę XMLFilterImpl). Obiekt taki można umieścić między parserem a ContentHandler-em, z punktu widzenia „handlera” działa on jak parser (można wywołać na nim metodę parse, on wywołuje metody „handlera”), natomiast z punktu widzenia parsera zachowuje się on jak „handler” – parser wywołuje metody filtra.

Filtry można łączyć w łańcuchy (metoda setParent), każdy filtr może filtrować lub modyfikować zdarzenia, a także wykonywać pewne czynności / obliczenia. Dzięki temu podczas jednego parsowania dokumentu można wykonać wiele czynności.

Przykład 4. Łączenie parsera i handlera poprzez filtr

XMLReader parser = XMLReaderFactory.getXMLReader();
ContentHandler handler = new MyHandler();
XMLFilter filtr = new MyFilter();
filtr.setParent(parser);
filtr.setContentHandler(handler);
filtr.parse();

Przykład 5. FiltrySAX

Plik: FiltrySAX.

Prosty przykład użycia filtra SAX. Filtr przepuszcza tylko niepuste węzły tekstowe i zmienia wielkość liter w nazwach elementów.

Zadanie 2.

Zadanie dotyczy przykładu z wykładu i dokumentów takich jak liczby.xml.

Wykorzystaj klasę LiczbyFiltr, która implementuje filtr SAX przepuszczający tylko grupy o atrybucie ważne równym tak i ich zawartość (podelementy i tekst), natomiast zatrzymujący nieważne grupy i ich zawartość.

  1. Połącz filtr z napisanym wcześniej PrintHandler i ułóż program wypisujący przefiltrowany dokument z liczbami.
  2. Wykorzystaj filtr do implementacji programu zliczającego zawartość elementów l w ważnych grupach.
  3. Niech powyższy filtr dodatkowo dodaje na koniec każdej ważnej grupy element suma zawierający sumę liczb z tej grupy.

Walidacja

Dokumenty można walidować podczas parsowania w trybie SAX.

Najprościej walidować dokument względem jego DTD (wystarczy ustawić w fabryce parserów setValidating(true)). Walidacja względem XML Schema wymaga niewielkich dodatkowych zabiegów (podobnych jak dla DOM).


Valid XHTML 1.1Valid CSS