CodeGym /Kursy /JAVA 25 SELF /StAX (Streaming API for XML) — strumieniowe parsowanie

StAX (Streaming API for XML) — strumieniowe parsowanie

JAVA 25 SELF
Poziom 47, Lekcja 2
Dostępny

1. Wprowadzenie do parsowania strumieniowego

W Javie historycznie ukształtowały się dwa podejścia do pracy z XML.

Wiemy już, że DOM (Document Object Model) buduje w pamięci drzewo dokumentu. Wygodnie jest poruszać się po elementach i je edytować, ale dla dużych plików jest to zbyt kosztowne: pamięć szybko się kończy.

Z kolei SAX (Simple API for XML) przetwarza plik sekwencyjnie i wywołuje zdarzenia przy napotkaniu tagów. To oszczędne pod względem pamięci — można pracować z ogromnymi dokumentami. Jednak pisanie handlerów bywa niewygodne, a powrót do poprzedniej struktury jest niemożliwy.

StAX (Streaming API for XML) pojawił się jako kompromis. Jest również strumieniowy, jak SAX, ale daje programiście większą kontrolę: sami „ciągniemy” zdarzenia ze strumienia, gdy jest to potrzebne. Takie podejście nazywa się parsowaniem pull i pozwala pisać bardziej zrozumiały i elastyczny kod.

Wprowadzenie do StAX

StAX (Streaming API for XML) — to nowoczesny strumieniowy parser XML dla Javy, wprowadzony w JDK 6+.
Główna idea: parsowanie pull („ciągnący” parser).

W przeciwieństwie do SAX, gdzie parser sam wywołuje twoje metody (model push), w StAX to ty zarządzasz procesem:
Sam prosisz parser: „Daj mi następne zdarzenie!”

Analogicznie:
SAX — to jak telewizja: zdarzenia „płyną” do ciebie, musisz reagować.
StAX — to jak serwis, w którym sam naciskasz „następne wideo”, kiedy jesteś gotów.

Kluczowe klasy StAX

Aby pracować ze StAX, potrzebne są dwie główne klasy z pakietu javax.xml.stream:

  • XMLInputFactory — fabryka do tworzenia parserów.
  • XMLStreamReader — sam parser strumieniowy, który czyta XML „po kawałkach”.

Przykład podstawowego kodu:

import javax.xml.stream.*;
import java.io.FileInputStream;

XMLInputFactory factory = XMLInputFactory.newInstance();
XMLStreamReader reader = factory.createXMLStreamReader(new FileInputStream("data.xml"));

while (reader.hasNext()) {
    int event = reader.next();
    // obsługa zdarzenia
}
reader.close();

2. Zasada działania StAX: model pull

W StAX to ty sam zarządzasz odczytem XML:

  1. Otwierasz strumień (np. plik).
  2. Tworzysz XMLStreamReader.
  3. W pętli wywołujesz reader.next(), aby pobrać następne zdarzenie.
  4. Sprawdzasz typ zdarzenia (START_ELEMENT, END_ELEMENT, CHARACTERS, itd.).
  5. Gdy dojdziesz do odpowiedniego miejsca — przetwarzasz dane.
  6. Zamykasz parser.

Schemat działania:

flowchart TD
    A[Otwórz XMLStreamReader] --> B{hasNext?}
    B -- tak --> C["next()"]
    C --> D{Jaki typ zdarzenia?}
    D -- START_ELEMENT --> E[Obsługa początku elementu]
    D -- CHARACTERS --> F[Obsługa tekstu]
    D -- END_ELEMENT --> G[Obsługa końca elementu]
    D -- END_DOCUMENT --> H[Zakończ]
    B -- nie --> H

Dlaczego to wygodne?

  • Sam decydujesz, kiedy czytać kolejny element.
  • Można „zatrzymać się” w potrzebnym miejscu i przetworzyć tylko część pliku.
  • Nie trzeba pisać mnóstwa handlerów, jak w SAX.

3. Typy zdarzeń w StAX

Kiedy wywołujesz reader.next(), parser zwraca typ zdarzenia — liczbę całkowitą (stała z interfejsu XMLStreamConstants). Oto podstawowe typy zdarzeń:

  • START_ELEMENT — początek elementu XML (<tag>).
  • END_ELEMENT — koniec elementu XML (</tag>).
  • CHARACTERS — treść tekstowa między tagami.
  • END_DOCUMENT — koniec dokumentu.

Przykład obsługi zdarzeń:

while (reader.hasNext()) {
    int event = reader.next();
    switch (event) {
        case XMLStreamConstants.START_ELEMENT:
            String name = reader.getLocalName();
            System.out.println("Początek elementu: " + name);
            break;
        case XMLStreamConstants.CHARACTERS:
            String text = reader.getText().trim();
            if (!text.isEmpty()) {
                System.out.println("Tekst: " + text);
            }
            break;
        case XMLStreamConstants.END_ELEMENT:
            System.out.println("Koniec elementu: " + reader.getLocalName());
            break;
    }
}

4. Przydatne uwagi

Kiedy używać StAX?

StAX to idealny wybór, jeśli:

  • Plik XML jest bardzo duży (gigabajty) i nie chcesz ładować go w całości do pamięci.
  • Trzeba przetworzyć tylko część dokumentu (np. znaleźć określony element i się zatrzymać).
  • Potrzebny jest prosty i zrozumiały kod: StAX jest prostszy niż SAX i nie wymaga pisania mnóstwa handlerów.

Przykładowe zadania:

  • Import dużego pliku XML z danymi (np. eksport z 1C, wyciągi bankowe, katalogi produktów).
  • Wyszukiwanie i przetwarzanie tylko potrzebnych elementów (np. jedynie <transaction> z miliona wpisów).
  • Przekształcanie XML „w locie” (np. filtrowanie, agregacja).

Porównanie DOM, SAX i StAX

Podejście Pamięć Prostota Elastyczność Kiedy używać
DOM Duża (wszystko w pamięci) Bardzo prosty Można modyfikować drzewo Małe/średnie pliki, gdy trzeba edytować XML
SAX Minimalna Trudny (obsługa zdarzeń) Tylko odczyt, brak możliwości cofania Bardzo duże pliki, prosta obróbka
StAX Minimalna Średnia (model pull) Można czytać partiami, łatwo przerywać Duże pliki, gdy potrzebna jest elastyczność i prostota

StAX — złoty środek:
— Nie zużywa tyle pamięci co DOM.
— Nie wymaga skomplikowanych procedur obsługi zdarzeń, jak SAX.
— Pozwala kontrolować proces parsowania.

5. Przykład: odczyt dużego pliku XML za pomocą StAX

Załóżmy, że mamy plik "books.xml":

<library>
    <book>
        <title>Java dla początkujących</title>
        <author>Ivan Ivanov</author>
    </book>
    <book>
        <title>Zaawansowana Java</title>
        <author>Pyotr Petrov</author>
    </book>
    <!-- ... wiele książek ... -->
</library>

Zadanie: wypisać wszystkie tytuły książek.

Kod w StAX:

import javax.xml.stream.*;
import java.io.*;

public class StaxDemo {
    public static void main(String[] args) throws Exception {
        XMLInputFactory factory = XMLInputFactory.newInstance();
        XMLStreamReader reader = factory.createXMLStreamReader(new FileInputStream("books.xml"));

        while (reader.hasNext()) {
            int event = reader.next();
            if (event == XMLStreamConstants.START_ELEMENT && "title".equals(reader.getLocalName())) {
                reader.next(); // przechodzimy do CHARACTERS
                System.out.println("Książka: " + reader.getText());
            }
        }
        reader.close();
    }
}

Zalety:

  • Nie ładujemy całego pliku do pamięci.
  • Można przetworzyć nawet milion książek — program się nie zawiesi.

6. Typowe błędy podczas pracy ze StAX

Błąd nr 1: zapomniano zamknąć parser lub strumień. Zawsze zamykaj XMLStreamReader i strumień (InputStream), aby nie dochodziło do wycieków zasobów.

Błąd nr 2: brak sprawdzenia typu zdarzenia. Nie wszystkie zdarzenia to początek lub koniec elementu. Sprawdzaj typ zdarzenia, w przeciwnym razie możesz uzyskać puste ciągi lub pominąć potrzebne dane.

Błąd nr 3: nieuwzględnienie zagnieżdżenia elementów. Jeśli struktura XML jest złożona (np. książki wewnątrz sekcji), kontroluj bieżący poziom zagnieżdżenia, aby nie pomylić elementów.

Błąd nr 4: używanie DOM do dużych plików. Jeśli plik jest duży — nie używaj DOM, w przeciwnym razie możesz otrzymać OutOfMemoryError. Dla dużych plików — tylko StAX lub SAX.

Błąd nr 5: brak obsługi wyjątków. Praca z plikami i XML może zgłaszać wyjątki (XMLStreamException, IOException). Obsługuj je lub propaguj wyżej.

1
Zadanie
JAVA 25 SELF, poziom 47, lekcja 2
Niedostępne
Przygotowanie strumieniowej analizy danych ⚙️
Przygotowanie strumieniowej analizy danych ⚙️
1
Zadanie
JAVA 25 SELF, poziom 47, lekcja 2
Niedostępne
Szczegółowe badanie struktury dokumentu XML 🕵️‍♀️
Szczegółowe badanie struktury dokumentu XML 🕵️‍♀️
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION