%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% :SAX %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{SAX} SAX (Simple API for XML) je snadno srozumitelný a~lehce uchopitelný nástroj pro čtení a~zpracování XML dokumentů. SAX je navržen jako událostmi řízený nástroj. Čtený XML dokument je zde pojat jako proud (stream), který prochází čtecím objektem. Ten při čtení klíčových informací volá události. Programátorovi pak stačí napsat ošetření pro tyto události ve formě zpracování právě přečtených dat. SAX sám o~sobě neumožňuje návraty v~čtení dokumentu, v~některých implementacích (konkrétně například pro Javu) však existuje možnost jak toto obejít (viz. metoda \texttt{characters}). \subsection{Načtení dokumentu} Ze všeho nejdřív musíme načíst XML dokument. K~tomu budeme potřebovat třídy \texttt{XMLReaderFactory} a~\texttt{XMLReader} z~balíčků \texttt{org.xml.sax.helpers} a~\texttt{org.xml.sax} (v~tomto pořadí). Instanci třídy \texttt{XMLReader} získáme pomocí statické tovární metody \texttt{createXMLReader} třídy \texttt{XMLReaderFactory}. Této instanci musíme nastavit objekt pro ošetření událostí (v~našem případě jsme příslušnou třídu pojmenovali \texttt{AnimalHandler} -- její implementaci si ukážeme za chvíli). Parsování dat pak spustíme zavoláním metody \texttt{parse}. Tato metoda bere jako parametr objekt \texttt{InputSource} z~balíčku \texttt{org.xml.sax}, který dále bere jako parametr objekt typu \texttt{InputStream}. Můžeme tedy parsovat jak proud XML dat, tak XML soubor (\texttt{FileInputStream}) nebo XML text (\texttt{StringReader}). V~následujícím příkladu si ukážeme, jak zavolat zpracování XML souboru \emph{canis\_lupus.xml}. \begin{Verbatim} try \{ XMLReader parser = XMLReaderFactory.createXMLReader(); AnimalHandler handler = new AnimalHandler(); parser.setContentHandler(handler); parser.parse(new InputSource(new FileInputStream( new File("canis_lupus.xml")))); Animal wolf = handler.getResult(); \} catch (SAXException ex) \{ System.err.println("Cannot process XML file"); \} catch (IOException ex) \{ System.err.println("Cannot open file"); \} \end{Verbatim} \subsection{Třída pro ošetření událostí} Třída pro ošetření událostí (event handler) musí implementovat rozhraní \texttt{ContentHandler} z~balíčku \texttt{org.xml.sax}. Toto rozhraní nás ale nutí naimplementovat velké množství ošetřujících metod. Přitom většinu z~nich nebudeme při běžném zpracování XML dokumentu potřebovat. Lepší řešení je tedy využít pomocnou třídu \texttt{DefaultHandler} z~balíčku \texttt{org.xml.sax.helpers}, která zmíněné rozhraní implementuje a~všechny tyto metody obsahuje s~prázdným tělem. Naši třídu pro ošetření událostí můžeme vytvořit jako potomka třídy \texttt{DefaultHandler} a~přepsat pouze ty metody, které budeme potřebovat. Výsledný kód bude vypadat takto: \begin{Verbatim} public class AnimalHandler extends DefaultHandler \{ private Animal result; public Animal getResult() \{ return result; \} \} \end{Verbatim} \subsection{startElement} V~reakci na událost přečtení otevírajícího tagu elementu vyvolá SAX metodu \texttt{startElement} u~naší ošetřující třídy. Jako parametry této metody pak dosadí podle toho, co právě přečetl, hodnoty: jmenný prostor, jméno elementu, kvalifikované jméno elementu (QName) a~seznam XML atributů elementu. Předpokládejme, že v~XML dokumentu narazíme na otevírací tag \texttt{}. Pak SAX zavolá metodu \texttt{startElement} a~jako parametry dosadí: \texttt{""} (jmenný prostor), \texttt{"animal"} (jméno), \texttt{"animal"} (QName) a~objekt typu \texttt{AttributesImpl} reprezentující prázdný seznam atributů. \begin{Verbatim} public class AnimalHandler extends DefaultHandler \{ private Animal result; public Animal getResult() \{ return result; \} \codeHighlight{@Override} \codeHighlight{public void startElement(String uri, String localName, } \codeHighlight{ String qName, Attributes attributes) \{} \codeHighlight{ if (localName.equals("animal")) \{} \codeHighlight{ result = new Animal();} \codeHighlight{ \}} \codeHighlight{\}} \} \end{Verbatim} \subsection{characters} Metoda \texttt{characters} je volána v~reakci na událost přečtení hodnoty XML elementu. Jako hodnoty jejích parametrů SAX posílá: pole znaků, index začátku přečtené hodnoty vzhledem k~tomuto poli a~délku přečtené hodnoty ve znacích. Zaslané pole znaků vždy obsahuje právě přečtenou hodnotu elementu. V~některých implementacích SAXu (včetně té pro Javu) ale navíc obsahuje i~předchozí přečtená XML data (jako prostý text, tedy včetně tagů ap.). To lze využít ke zpětnému dohledání informací. Pro běžné zpracování bude ale tělo této metody \texttt{characters} vypadat téměř vždy takto. \begin{Verbatim} public class AnimalHandler extends DefaultHandler \{ \codeHighlight{private String buffer;} private Animal result; public Animal getResult() \{ return result; \} @Override public void startElement(String uri, String localName, String qName, Attributes attributes) \{ if (localName.equals("animal")) \{ result = new Animal(); \} \} \codeHighlight{@Override} \codeHighlight{public void characters(char[] chars, int start, int length) \{} \codeHighlight{ buffer = String.copyValueOf(chars, start, length);} \codeHighlight{\}} \} \end{Verbatim} \subsection{endElement} Když narazí SAX na ukončovací tag XML elementu, zavolá metodu \texttt{endElement} a~jako parametry ji předá jmenný prostor, kvalifikované jméno elementu a~obyčejné jméno elementu. Metoda \texttt{endElement} se zpravidla používá na to, abychom zkopírovali obsahu bufferu do sestavovaného objektu. V~této metodě se běžně vyskytují větvení typu \texttt{if-else-if}. \begin{Verbatim} public class AnimalHandler extends DefaultHandler \{ \codeHighlight{private String buffer;} private Animal result; public Animal getResult() \{ return result; \} @Override public void startElement(String uri, String localName, String qName, Attributes attributes) \{ if (localName.equals("animal")) \{ result = new Animal(); \} \} @Override public void characters(char[] chars, int start, int length) \{ buffer = String.copyValueOf(chars, start, length); \} \codeHighlight{@Override} \codeHighlight{public void endElement(String uri, } \codeHighlight{ String localName, String qName) \{} \codeHighlight{ if (localName.equals("genus")) \{} \codeHighlight{ result.setGenus(buffer);} \codeHighlight{ \}} \codeHighlight{ else if (localName.equals("species")) \{} \codeHighlight{ result.setSpecies(buffer);} \codeHighlight{ \}} \codeHighlight{\}} \} \end{Verbatim} \subsection{Vytvoření XML} SAX umožňuje také vytvořit XML dokument. Nejdříve potřebujeme získat instanci třídy \texttt{ContentHandler} která bude mít odkaz na výchozí proud. Na této instanci pak voláme stejné metody, jaké jsme přepisovali v~naší třídě \texttt{AnimalHandler}. Hodnoty parametrů zadáváme stejným způsobem, jakým je SAX dosazoval při čtení XML dokumentu. Pro vytvoření jednoduchého XML dokumentu obsahujícího objekt vlka obecného bychom museli napsat následující kód. Všimněte si hlavně sekvenčního volání metod na objektu \texttt{handler} v~druhé části kódu. \begin{Verbatim} FileOutputStream stream = new FileOutputStream("canis_lupus.xml"); OutputFormat format = new OutputFormat("XML", "UTF-8",true); XMLSerializer serializer = new XMLSerializer(stream, format); ContentHandler handler = serializer.asContentHandler(); handler.startDocument(); handler.startElement("", "", "animal", new AttributesImpl()); handler.startElement("", "", "genus", new AttributesImpl()); handler.characters("canis".toCharArray(), 0, "canis".length()); handler.endElement("", "", "genus"); handler.startElement("", "", "species",new AttributesImpl()); handler.characters("lupus".toCharArray(), 0, "lupus".length()); handler.endElement("", "", "species"); handler.endElement("", "", "animal"); handler.endDocument(); \end{Verbatim} Vidíte, že i~k~vytvoření malého XML dokumentu se třemi elementy je potřeba značné množství kódu. Navíc volaným metodám musíme často předávat prázdné řetězce nebo nadbytečné informace (délka řetězce v~metodě \texttt{characters}). Namísto SAXu je tak daleko častěji lepší sestavovat XML textově nebo použít jiného nástroje, například DOM. \subsection{Závěr} SAX představuje velmi pohodlný nástroj pro čtení a~zpracování XML dokumentu. Mezi jeho výhody patří velmi rychlé proudové čtení dokumentu. Zároveň pokud nechceme zpracovávat celý dokument, ale pouze najít určitou část (vyhledávání ve slovníku, konfiguračním souboru ap.), pak SAX umožní toto provést rychle a~efektivně. Mezi nevýhody můžeme zařadit masivní konstrukce \texttt{if--else} u~zpracování dokumentů s~bohatou XML gramatikou. K~vytváření XML dokumentů se SAX nehodí. Použitá literatura:\\ \cite{XML v kostce} \emph{XML v kostce}\\ \cite{XML Reference} \emph{XML -- pohotová referenční příručka}\\ \cite{SAX Wikipedia} \emph{SAX na Wikipedii}\\