piątek, 1 maja 2009

10 minut z biblioteką SAX - Simple API for XML

Przy okazji projektu z sieci komputerowych przyszło mi szukać biblioteki do parsowania XML, która będzie działać na wszystkich 3 platformach Java. Długo się nie zastanawiając wybrałem SAX. Miałem już dość męczenia się z jakimś kXML czy jakimś innym wytworem i wybrałem coś konkretnego. Jaki był tego koszt ? Moja aplikacja na komórkę będzie wymagać implementacji Java ME Web Service (JSR 172). No ale żałować nie będę, bo bez tego już chyba urządzeń mobilnych z Javą nie wprowadzają na rynek.

Ok, ale przejdźmy do zadania :) Trzeba parsować proste komunikaty XML o postaci:
<?xml version=”1.0” encoding=”utf-8”?>
<response value=”[odpowiedz]”>
<params>
<[klucz] value=”[wartość]”/>
<!-- inne parametry takiej postaci -->
</params>
</response>

Nie wygląda na trudne. Ale zobaczmy jak to się robi z użyciem SAX.
W pierwszej kolejności musimy utworzyć klasę handlera który będzie odpowiadać za obsługę poszczególnych elementów dokumentu XML. Klasa handlera musi dziedziczyć po klasie DefaultHandler, która umożliwia implementacje kilku bardzo przydatnych metod. Nasz handler będzie posiadać następujące:
- public void startElement(String uri, String localName, String qName, Attributes attribs) - metoda ta wywoływana jest gdy parser natrafi na znacznik otwierający
- public void endElement(String uri, String localName, String qName) - metoda ta zostanie wywołana po natrafieniu na znacznik zamykający
W tym miejscu dodam jeszcze notkę o metodzie characters chociaż jej nie będziemy używać jest dość istotna. Ma następującą sygnaturę:
- public void characters(char[] ch, int start, int lenght) - wywoływana jest gdy parser natrafi na tekst (tekstowa zawartość węzła)

A oto znaczenie poszczególnych parametrów:
uri - namespace uri
localName - lokalna nazwa znacznika (bez prefiksu)
qName - nazwa kwalifikowana znacznika (z prefiksem)
attribs - obiekt typu Attributes zawierający atrybuty danego znacznika
ch - tablica znaków
start - pozycja startowa w tablicy znaków
lenght - liczba znaków
To są absolutnie podstawowe metody które należy znać. Po więcej zapraszam do dokumentacji DeafultHadler.

Ok, to możemy przystąpić do implementacji klasy.
package com.blogspot.wookasz.saxexample;

import java.util.HashMap;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

class SAXMessageHandler extends DefaultHandler {
// flaga oznaczająca czy teraz wczytywane będą parametry
private boolean paramMode = false;
// parametry
private HashMap<String, String> params;
// wartość odpowiedzi w komunikacie
private String response;

public SAXMessageHandler() {
super();
this.params = new HashMap<String, String>();
}

@Override
public void startElement(String uri, String localName, String qName,
Attributes attribs) throws SAXException {
// jeśli natrafiono na węzeł z odpowiedzią
if (qName.equals("response") && !paramMode) {
response = attribs.getValue("value");
return;
}
// jeśli natrafiono na węzęł rozpoczynający parametry
if (qName.equals("params")) {
paramMode = true; // ustawiamy flagę że teraz będą parametry
return;
}

// jeśli ustawiona jest flaga parametrów
if (paramMode) {
// dodajemy parametr z wartością do mapy
params.put(qName, attribs.getValue("value"));
return;
}
}

@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
// jeśli włączyony był tryb parametrów
// i nastrafiono na zamykający go znacznik
// to zmieniamy flegę
if (qName.equals("params")) {
paramMode = false;
}
}

public HashMap<String, String> getParams() {
return params;
}

public String getResponse() {
return response;
}
}

Komentarze w kodzie myślę wyjaśniają całkowicie działanie ;) Jakby co komentować!
Teraz czas na utworzenie parsera i zarejestrowanie handlera:)
To już tylko kilka linijek:
// utworzenie fabryki SAX
SAXParserFactory saxFactory = SAXParserFactory.newInstance();
// utworzenie parsera
SAXParser parser = saxFactory.newSAXParser();
// utworzenie obiektu czytającego wiadomości XML
XMLReader reader = parser.getXMLReader();

// utworzenie naszego handlera
MessageSAXHandler xmlHandler = new SAXMessageHandler();
// oraz ustawienie go jako domyślnego dla obiektu czytającego
reader.setContentHandler(xmlHandler);

Teraz możemy poprzez metodę reader.parse(InputStram input) parsować wiadomości które do nas przychodzą. Należy zwrócić uwagę na to, że parametrem jest obiekt typu InputSource (dostępna jest jeszcze implementacja tej metody przyjmująca String - URI)! Jeśli mamy już całą wiadomość w pamięci to możemy to obejść w prosty sposób:
String xmlMsg = "[wiadomosc]";
InputStream streamXml = new ByteArrayInputStream(xmlMsg.getBytes()); // utworzenie strumienia bajtów z wiadomości
reader.parse(new InputSource(streamXml)); // parsowanie

Powinno działać :)

Jeszcze dodam uwagę co do implementacji tej biblioteki na platformie Java ME. JSR-172 trochę obcięło API i nie możemy utworzyć obiektu typu XMLReader. Na tej platformie parsowanie wygląda następująco:
SAXParserFactory saxFactory = SAXParserFactory.newInstance();
SAXParser parser = saxFactory.newSAXParser();

SAXMessageHandler xmlHandler = new SAXMessageHandler();

parser.parse(new InputSource(streamXml), xmlHandler);

Oraz w klasie SAXMessageHandler należy zmienić typ zmiennej params na Hashmap.
To rozwiązanie powinno także działać w Java SE oraz Java EE ;-)

Mam nadzieję, że ten krótki tutorial komuś pomoże rozpocząć pracę z biblioteką SAX. Po więcej informacji zapraszam na stronę www.saxproject.org.

3 komentarze:

  1. Witam. Otóż zainteresował mnie temat , tylko mam jeden problem. W momencie kiedy mam dodać HashMapę do projektu. Nie wiem dlaczego ale wyrzuca błąd cannot find the symbol.
    Jeśli chodzi o JSE to mam wersję 1.6 i patrzyłem tam znajduje się HashMapa. Gdzie może tkwić mój błąd ??

    OdpowiedzUsuń
  2. Witam!
    Nie mam pojęcia dlaczego takie coś u Ciebie występuje. A próbowałeś ręcznie dodać import ? Jakiego używasz IDE ?

    OdpowiedzUsuń
  3. trzeba sprawdzic:)

    OdpowiedzUsuń