XML – ćwiczenia 15: Parsowanie strumieniowe i JAXB.

Wszystkie przykładowe programy (zawartość uległa zmianie):

Referencje

Oba omawiane dzisiaj standardy powstały (JAXB wcześniej, StAX później) w związku z „webserwisami”. Są częścią Java Web Services Developer Pack, Java Enterprise Edition i projektu GlassFish, a od wersji 6 także Java Standard Edition. Ponadto StAX został dodany do wersji 1.4 standardu JAXP.

Parsowanie strumieniowe

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.

Streaming API for XML (StAX)

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).

Niedawno 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:

  • za pomocą kursora – interfejsy ze 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, nadaje się więc do takich zastosowań jak aplikacje J2ME.
  • za pomocą zdarzeń – interfejsy z 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.

Przykład 1.

Pliki: LiczbyStax1.java, LiczbyStax2.java.

Przykład o sumowaniu liczb w StAX, dwie wersje: z kursorem i ze zdarzeniami.

Tworzenie wyniku w StAX

StAX pozwala także tworzyć dokumenty, służą do tego interfejsy XMLStreamWriter i XMLEventWriter.

Przykład 2. Tworzenie wyniku przy pomocy 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).

Filtry StAX

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.

Przykład 3.

Ź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());
...

Zadanie 1.

Napisz program, który (poprzez interfejs StAX) odczyta "godzinki" z zadania zaliczeniowego 3 i wypisze (korzystając z interfejsu StAX) krótki dokument XML zawierający:

  1. liczbę wpisów
  2. sumaryczną liczbę godzin
  3. najwcześniejszą i najpóźniejszą datę

Wiązanie XML (w Javie)

Wiązanie XML w języku programowania (u nas w Javie) to pomysł, aby schematom dokumentów XML (DTD, XML Schema itp.) odpowiadały hierarchie klas, a instancjom dokumentów konkretne obiekty (z obiektami – podelementami itd., tworząc strukturę drzewiastą).

Dokument zgodny ze schematem można wczytać do pamięci (unmarshalling), a drzewo obiektów zapisać do dokumentu (marshalling).

Interfejs dostępu do drzewa jest zależny od schematu, ale dzięki temu jest wygodniejszy. Np. elementowi osoba, który może zawierać jeden podelement nazwisko i wiele podelementów imię (oba typu xs:string) będzie odpowiadała klasa z metodami List<String> getImię() i String getNazwisko(). Ponadto wartości od razu są tego typu Javy, który odpowiada zadeklarowanemu typowi XML Schema.

Nie ma potrzeby pamiętania nazw elementów i atrybutów (po prostu widać jakiego są typu), w wielu przypadkach można też uniknąć tworzenia list i słowników, dzięki temu wiązanie XML jest mniej pamięciochłonne i szybsze niż DOM.

JAXB

Standard Java API for XML Binding jest realizacją idei „wiązania XML” w Javie, został opracowany przez Suna, obecnie jest projektem open-source.

Niedawno JAXB został włączony do platformy Java Standard Edition (do wersji 6).

Od wersji JAXB 2.0 istnieje także możliwość generowania schematu na podstawie klas Javy (aby mieć większą kontrolę nad tym procesem, należy używać specjalnych anotacji w Javie).

Typowe kroki implementacji aplikacji używającej JAXB

  1. Przygotowanie schematu dokumentów.
  2. Kompilacja schematu narzędziem XJC, generuje klasy Javy odpowiadające typom zdefiniowanym w schemacie.

  3. Napisanie samej aplikacji korzystając z:

    • uniwersalnej części API JAXB,
    • klas wygenerowanych przez XJC.
  4. Uwaga! Zmiana schematu po napisaniu aplikacji może spowodować konieczność znacznych zmian w kodzie.

Przykład 4.

Pliki: LiczbyJAXB.java, klasy wygenerowane przez XJC.

Przykład o sumowaniu liczb w JAXB.

Zadanie 2.

Skompiluj narzędziem xjc schemat z zadania 3.

Przykłady w tutorialach Suna

Ciekawsze przykłady można znaleźć w tutorialach:

Wygodne uruchomienie przykładów poprzez Anta wymaga skonfigurowania Java Enterprise Edition. Same programy powinny działać w czystym Java SE 6.


Valid XHTML 1.1Valid CSS