Wszystkie przykładowe programy:
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:
API Javy:
org.xml.sax
[.*
], javax.xml.parsers
.Zobacz także:
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.
Plik: StaffSAX.java.
Program liczy sumę zarobków osób z podanego stanowiska.
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.
Napisz program czytający dokument taki jak sklep.xml i dla podanego w parametrze id kategorii wypisujący nazwę i cenę najtańszego towaru w tej kategorii.
Write a program reading documents such as
sklep.xml
which will take a category id as an argument
and print:
the name (nazwa
) and the price (cena
)
of the cheapest article (towar
) in the given category.
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:
setNamespaceAware(false)
(tak naprawdę to jest domyślna wartość),qName
w metodzie startElement
i metodą getQName
dla atrybutów.Jeśli chcemy uwzględniać przestrzenie nazw, powinniśmy:
setNamespaceAware(true)
,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.
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.
XMLReader parser = XMLReaderFactory.getXMLReader(); ContentHandler handler = new MyHandler(); XMLFilter filtr = new MyFilter(); filtr.setParent(parser); filtr.setContentHandler(handler); filtr.parse();
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.
Dotyczy przykładu sklep.
Napisz filtr SAX przepuszczający tylko kategorie i towary o podanym id-kategorii
(i ich zawartość).
Napisz ContentHandler liczący średnią cenę towaru. Połącz go z filtrem tak, aby program zliczał średnią cenę towaru w wybranej kategorii.
Parsowanie strumieniowe to sposób czytania dokumentu XML, w którym użytkownik parsera „prosi” parser o kolejne zdarzenie, o wczytanie kolejnego fragmentu dokumentu.
Daje to bardziej intuicjny przepływ sterowania niż w SAX, gdzie to parser „zarzuca” użytkownika zdarzeniami, które ten musi obsłużyć.
Jednocześnie zachowane są największe zalety SAX: szybkość (brak zbędnych czynności) i możliwość przetwarzania dokumentów nie mieszczących się w pamięci.
Standard Streaming API for XML jest realizacją idei „parsowania strumieniowego” w Javie, został opracowany w ramach JSR 173, a implementacja parsera była znana także jako Sun Java Streaming XML Parser (SJSXP).
StAX został dołączony do wersji 1.4 standardu JAXP i włączony do platformy Java Standard Edition (do wersji 6).
StAX pozwala na czytanie oraz zapisywanie dokumentów XML. Ze standardu można korzystać zasadniczo na dwa sposoby:
Stream
w nazwie.
Niejawnym kursorem przechodzimy do kolejnych węzłów dokumentu, o tym w jakim węźle
jesteśmy możemy się dowiedzieć metodą getEventType
, a inne metody get...
zwracają wartości (np. nazwę węzła, wartość węzła tekstowego / atrybutu). Ten sposób jest najbardziej
efektywny, nie zużywa niepotrzebnych zasobów, nie tworzy obiektu dla każdego odwiedzanego węzła.
Event
w nazwie.
Tutaj metodą nextEvent
pobieramy kolejne zdarzenie –
obiekt typu XMLEvent
. Różne zdarzenia będą miały różne konkretne typy
(zobacz pakiet javax.xml.stream.events
).
Ten sposób umożliwia bardziej „eleganckie” programowanie obiektowe
w zastosowaniach gdzie oszczędność zasobów nie jest aż tak istotna.
Plik: StaffStAX.java.
Program liczy sumę zarobków osób z podanego stanowiska.
StAX pozwala także tworzyć dokumenty, służą do tego interfejsy XMLStreamWriter
i XMLEventWriter
.
XMLStreamWriter
Źródło: Tutorial JEE
XMLOutputFactory output = XMLOutputFactory.newInstance(); XMLStreamWriter writer = output.createXMLStreamWriter( ... ); writer.writeStartDocument(); writer.setPrefix("c","http://c"); writer.setDefaultNamespace("http://c"); writer.writeStartElement("http://c","a"); writer.writeAttribute("b","blah"); writer.writeNamespace("c","http://c"); writer.writeDefaultNamespace("http://c"); writer.setPrefix("d","http://c"); writer.writeEmptyElement("http://c","d"); writer.writeAttribute("http://c","chris","fry"); writer.writeNamespace("d","http://c"); writer.writeCharacters("Jean Arp"); writer.writeEndElement(); writer.flush();
Program jest odpowiedzialny za przekazywanie zdarzeń w odpowiedniej kolejności, m.in. zamykanie otwartych elementów. Writer po otrzymaniu zdarzenia startElement czeka z wypisaniem znacznika otwierającego dopóki otrzymuje zdarzenia związane z atrybutami lub przestrzeniami nazw.
Interfejsu XMLEventWriter
używa sie podobnie, z tym że konsumuje obiekty–zdarzenia
(XMLEvent
). To podejście jest wygodne szczególnie wtedy, gdy większość zapisywanych węzłów pochodzi
z dokumentu czytanego na wejściu, a tylko niektóre węzły są pomijane lub tworzone od nowa (modyfikować istniejących
obiektów XMLEvent
akurat nie można). Wówczas wystarczy w pętli czytać zdarzenia z XMLEventReader
-a
i zapisywać do XMLEventWriter
-a.
Interfejsy StreamFilter
i EventFilter
służą do implementacji filtrów,
które w prosty sposób pozwalają przepuszczać tylko niektóre zdarzenia. XMLInputFactory
pozwala tworzyć parsery od razu filtrujące zdarzenia.
Źródło: Tutorial JEE
public class MyStreamFilter implements javax.xml.stream.StreamFilter { public boolean accept(XMLStreamReader reader) { if (!reader.isStartElement() && !reader.isEndElement()) return false; else return true; } } ... FileInputStream fis = new FileInputStream(filename); XMLStreamReader xmlr = xmlif.createFilteredReader( xmlif.createXMLStreamReader(fis), new MyStreamFilter()); ...
Plik content.xml to dokument z zawartością arkusza Open Document (cały plik ods to zip).
Napisz program, który czytając plik poprzez interfejs StAX (najlepiej EventReader
):
Korzystając z EventReader
i EventWriter
napisz program, który: