6. díl - Čtení XML souborů SAXem v Javě

Java Práce se soubory Čtení XML souborů SAXem v Javě

V minulém dílu našeho seriálu tutoriálů o Javě jsme si představili formát XML a ukázali si, jak pomocí SAXu vytvořit jednoduché XML. Nyní na minulý díl navážeme a napíšeme si proces opačný, tedy načtení XML souboru s uživateli a sestavení příslušné objektové struktury (listu uživatelů).

Pro úplnost si opět uvedeme náš XML soubor soubor.xml:

<?xml version="1.0" ?>
<uzivatele>
  <uzivatel vek="22">
    <jmeno>Pavel Slavík</jmeno>
    <registrovan>21.březen 2000 11:51</registrovan>
  </uzivatel>
  <uzivatel vek="31">
    <jmeno>Jan Novák</jmeno>
    <registrovan>30.říjen 2012 11:51</registrovan>
  </uzivatel>
  <uzivatel vek="16">
    <jmeno>Tomáš Marný</jmeno>
    <registrovan>1.leden 2011 11:51</registrovan>
  </uzivatel>
</uzivatele>

A naši třídu Uzivatel.java:

public class Uzivatel
{
    private String jmeno;
    private int vek;
    private Calendar registrovan;
        public static DateFormat formatData = new SimpleDateFormat("d.MMMM yyyy H:mm");

    public Uzivatel(String jmeno, int vek, Calendar registrovan)
    {
                this.jmeno = jmeno;
                this.vek = vek;
                this.registrovan = registrovan;
    }

    @Override
    public String toString()
    {
                return getJmeno();
    }

    public String getJmeno() {
        return jmeno;
    }

    public int getVek() {
        return vek;
    }

    public Calendar getRegistrovan() {
        return registrovan;
    }

}

Založme si nový projekt, půjde opět o konzolovou aplikaci. Pojmenujeme ji XmlSaxCteni a do složky s projektem nakopírujeme náš XML soubor. K projektu připojíme také třídu Uzivatel. Uživatele budeme chtít načíst do nějaké kolekce, vytvořme si tedy prázdný ArrayList uzivatele. Kód budeme kvůli jednoduchosti psát do metody main(), jak to udělat dobře objektově jsme si v této sekci již ukazovali.

ArrayList<Uzivatel> uzivatele = new ArrayList<>();

Čtení XML přes SAX

Ke čtení XML přes SAX nám Java poskytuje třídu XMLStreamReader. Opět ji jako její protějšek XMLStreamReader vytvoříme pomocí továrny, kterou je pro zápis XMLInputFactory. Připravme si podobnou strukturu z try-catch bloků, jako minule. Bohužel opět nemůžeme použít try-with-resources, jelikož XMLStreamWriter neposkytuje potřebné rozhraní.

XMLInputFactory factory = XMLInputFactory.newInstance();
XMLStreamReader xsr = null;
try
{
         xsr = factory.createXMLStreamReader(new FileReader("soubor.xml"));
}
catch (Exception e)
{
        System.err.println("Chyba při čtení souboru: " + e.getMessage());
}
finally
{
        try
        {
                xsr.close();
        }
        catch (Exception e)
        {
                System.err.println("Chyba při uzavírání souboru: " + e.getMessage());
        }
}

V bloku try si otevřeme XML soubor pro čtení a právě pod tento řádek budeme přidávat další kód pro čtení ze souboru. Odchycení chyby a zavření souboru by mělo být z minula jasné.

Připravíme si pomocné proměnné pro atributy uživatele. Nemůžeme ukládat přímo do instance, protože třída nemá settery. Druhou možností může být settery přidat, tím ale ztrácíme část zapouzdření. Proměnné naplníme výchozími hodnotami, ty se dosadí v případě, že daná hodnota nebude v XML zapsána. Budeme potřebovat někam ukládat jméno aktuálního elementu, k tomu si zadefinujeme Stringovou proměnnou element. Kód píšeme pochopitelně do prvního try bloku pod dosazení do xsr.

String jmeno = "";
int vek = 0;
Calendar registrovan = Calendar.getInstance();
String element = "";

Začneme načítat soubor. XMLStreamReader načítá soubor řádek po řádku, odshora dolů. Na jeho instanci voláme metodu hasNext(). Ta nám vrátí true, pokud ještě nejsme na konci XML souboru. Uzlem může být element, případně atribut nebo textová hodnota elementu (nás bude zajímat START_ELEMENT, CHARACTERS a END_ELEMENT), dalším typem uzlu může být např. komentář, které pro nás nyní nebudou důležité. Postoupíme tedy k postupnému načítání všech uzlů v dokumentu pomocí while cyklu. Na konec cyklu umístíme volání metody next(), která přesune "hlavu" čtečky na další uzel. Kódem níže tedy projedeme celý dokument, uzel po uzlu:

while(xsr.hasNext())
{
        xsr.next();
}

Na instanci XMLStreamReaderu máme několik užitečných metod, budeme používat getEventType(), ve které je uložen typ aktuálního uzlu (Java to nazývá událostí), na kterém se čtečka nachází. Jednotlivé typy uzlů nalezneme jako konstanty na třídě XMLStreamConstants. Dále použijeme metody getName().get­LocalPart() pro získání jména aktuálního elementu a getText() pro získání textu v aktuálním uzlu.

Nás budou nyní tedy zajímat 3 typy uzlů (událostí), START_ELEMENT a CHARACTERS a END_ELEMENT. Pojďme na ně reagovat, zatím napíšeme prázdné podmínky:

// načítáme element
if (xsr.getEventType() == XMLStreamConstants.START_ELEMENT)
{
}
// načítáme hodnotu elementu
else if (xsr.getEventType() == XMLStreamConstants.CHARACTERS)
{
}
else if ((xsr.getEventType() == XMLStreamConstants.END_ELEMENT))
{
}

Nyní vložíme kód do první podmínky. Budeme tedy reagovat na načtení elementu. Je zde potřeba provést 2 akce.

Klíčovou akcí bude uložení názvu elementu do proměnné element. Tak budeme vzápětí schopni v druhé podmínce zjistit, kterého elementu je text, který právě čteme.

Pokaždé, když narazíme na element uzivatel, načteme atribut věk pomocí metody getAttributeVa­lue(), jejímž parametrem je index atributu. Atribut aktuálního elementu lze načíst takto jednoduše. S hodnotou to tak jednoduché není. Obsah první podmínky tedy bude vypadat takto:

element = xsr.getName().getLocalPart();
if (element.equals("uzivatel"))
{
        vek = Integer.parseInt(xsr.getAttributeValue(0));
}

Přejděme do další větve, tedy do zpracování hodnoty elementu. Zde využijeme předem uložené hodnoty názvu elementu, kterou si vložíme do switche. Podle elementu uložíme hodnotu do dané vlastnosti uživatele. Na konci této větve element vyprázdníme, protože jsme ho již zpracovali.

switch (element)
{
        case "jmeno":
                jmeno = xsr.getText();
                break;
        case "registrovan":
                registrovan = Calendar.getInstance();
                registrovan.setTime(Uzivatel.formatData.parse(xsr.getText()));
                break;
}
element = "";

Již jsme velmi blízko, bystřejší si jistě všimli, že uživatele nikde nepřidáváme. Jeho přidání nastane ve chvíli načtení uzavíracího elementu uzivatel. Přejděme tedy do poslední větve:

if ((xsr.getName().getLocalPart().equals("uzivatel")))
{
        uzivatele.add(new Uzivatel(jmeno, vek, registrovan));
}

Máme hotovo :)

Pro jistotu přikládám kompletní kód načtení souboru:

XMLInputFactory factory = XMLInputFactory.newInstance();
XMLStreamReader xsr = null;

try
{
        xsr = factory.createXMLStreamReader(new FileReader("soubor.xml"));

        String jmeno = "";
        int vek = 0;
        Calendar registrovan = Calendar.getInstance();
        String element = "";


        while(xsr.hasNext())
        {
                // načítáme element
                if (xsr.getEventType() == XMLStreamConstants.START_ELEMENT)
                {
                        element = xsr.getName().getLocalPart();
                        if (element.equals("uzivatel"))
                        {
                                vek = Integer.parseInt(xsr.getAttributeValue(0));
                        }
                }
                // načítáme hodnotu elementu
                else if (xsr.getEventType() == XMLStreamConstants.CHARACTERS)
                {
                        switch (element)
                        {
                                case "jmeno":
                                        jmeno = xsr.getText();
                                        break;
                                case "registrovan":
                                        registrovan = Calendar.getInstance();
                                        registrovan.setTime(Uzivatel.formatData.parse(xsr.getText()));
                                        break;
                        }
                        element = "";
                }
                // načítáme konec elementu
                else if ((xsr.getEventType() == XMLStreamConstants.END_ELEMENT))
                {
                        if ((xsr.getName().getLocalPart().equals("uzivatel")))
                        {
                                uzivatele.add(new Uzivatel(jmeno, vek, registrovan));
                        }
                }
                xsr.next();
        }

}
catch (Exception e)
{
        System.err.println("Chyba při čtení souboru: " + e.getMessage());
}
finally
{
        try
        {
                xsr.close();
        }
        catch (Exception e)
        {
                System.err.println("Chyba při uzavírání souboru: " + e.getMessage());
        }
}

Zbývá uživatele vypsat, abychom věděli, že jsme je načetli správně. Upravíme si metodu toString() ve třídě Uzivatel tak, aby vypisovala všechny hodnoty:

@Override
public String toString()
{
        return String.format("%s, %d, %s", jmeno, vek, formatData.format(registrovan.getTime()));
}

Uživatele jednoduše vypíšeme:

// výpis načtených objektů
for (Uzivatel u : uzivatele)
{
        System.out.println(u);
}

a výsledek:

Výsledek načtení objektů z XML SAXem v Javě

Pokud se vám načítání příliš nelíbilo, dám vám za pravdu. Zatímco generování nového XML souboru je SAXem velmi jednoduché a přirozené, načítání je opravdu krkolomné. Příště se podíváme na DOM, tedy objektový přístup k XML dokumentu.


 

Stáhnout

Staženo 307x (20 kB)
Aplikace je včetně zdrojových kódů v jazyce java

 

  Aktivity (1)

Článek pro vás napsal David Čápka
Avatar
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.

Jak se ti líbí článek?
Celkem (6 hlasů) :
55555


 


Miniatura
Předchozí článek
Zápis XML souborů SAXem v Javě
Miniatura
Všechny články v sekci
Práce se soubory v Javě

 

 

Komentáře

Avatar
Blackess
Člen
Avatar
Blackess:

Ahoj, díky za další ze skvělých a šikovných článků! Dobrá práce!
Mám jeden dotaz. Netýká se to vyloženě XML, ale třídy Calendar. Proč je potřeba znovu nastavovat proměnou registrovan ve switch bloku? Dělám obdobný případ a bez tohoto kódu je v proměné nějaký náhodný datum, ale nedokážu si vysvětlit proč?.. Díky

 
Odpovědět 1.3.2014 22:16
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Blackess
David Čápka:

Protože jinak bys nastavil datum, které se již někde použilo. getInstance() ti dá nové a ty chceš nové. V tomhle případě by se asi nic nestalo, ale někde by to mohlo vadit.

Odpovědět 21.3.2014 17:33
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Matěj Kripner
Redaktor
Avatar
Matěj Kripner:

Zdravím,
děkuji za další skvělý tutoriál, jako vždy mi přinesl spoustu nového. Mám jen takové dva dotazy:

  1. Nemělo by být při uzavírání souboru opodmínkováno, jestli se soubor vůbec podařilo otevřít(if(xsr != null))?
  2. Není lepší používat specializované výjimky místo obecné Exception?

Předem díky za odpověď.

Odpovědět 18.4.2014 11:48
"We reject kings, presidents and voting. We believe in rough consensus and running code" David Clark
Avatar
vratas
Člen
Avatar
vratas:

import com.sun.xml.in­ternal.txw2.ou­tput.Indentin­gXMLStreamWri­ter; mam stale podtrzeny, musi se to nejak pridat do knihoven?

 
Odpovědět 8.11.2015 15:29
Avatar
Richard H.
Redaktor
Avatar
Odpovídá na vratas
Richard H.:

Tohle by mělo být součástí standartních knihoven takže ne a nebo je článek již zastaralí jdu to skusit.

Odpovědět 8.11.2015 17:09
Malý užitečný manuál je vždy lepší než bichle k ničemu.
Avatar
Richard H.
Redaktor
Avatar
Odpovídá na vratas
Richard H.:

Mě to funguje co máš za javu ?

Odpovědět 8.11.2015 17:11
Malý užitečný manuál je vždy lepší než bichle k ničemu.
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 6 zpráv z 6.