Hľadáme nové posily do ITnetwork tímu. Pozri sa na voľné pozície a pridaj sa k najagilnejšej firme na trhu - Viac informácií.
IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

8. diel - Čítanie XML súborov SAXom v Jave

V minulej lekcii, Zápis XML súborov SAXom v Jave , sme si predstavili formát XML a ukázali si, ako pomocou SAXu vytvoriť jednoduché XML.

V dnešnom Java tutoriále si ukážeme načítanie XML súboru s užívateľmi a zostavenie príslušnej objektovej štruktúry (listu užívateľov).

Založme si nový projekt, pôjde opäť o konzolovú aplikáciu. Pomenujeme ju XmlSaxCteni.

Súbor soubor.xml

Do adresára itnetwork, v domovskom adresári, nakopírujeme tento náš XML súbor soubor.xml:

<?xml version="1.0" ?>
<uzivatele>
    <uzivatel vek="22">
        <jmeno>Pavel Slavík</jmeno>
        <registrovan>21.March 2000</registrovan>
    </uzivatel>
    <uzivatel vek="31">
        <jmeno>Jan Novák</jmeno>
        <registrovan>30.October 2012</registrovan>
    </uzivatel>
    <uzivatel vek="16">
        <jmeno>Tomáš Marný</jmeno>
        <registrovan>1.January 2011</registrovan>
    </uzivatel>
</uzivatele>

Trieda Uzivatel

A tu je naša trieda Uzivatel:

public class Uzivatel {
    private final String jmeno;
    private final int vek;
    private final LocalDate registrovan;
    public static DateTimeFormatter formatData = DateTimeFormatter.ofPattern("d'.'LLLL yyyy");

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

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

    public String getJmeno() {
        return jmeno;
    }

    public int getVek() {
        return vek;
    }

    public LocalDate getRegistrovan() {
        return registrovan;
    }
}

Trieda Konstanty

Než sa presunieme k samotnému čítaniu, vytvoríme si pomocnú triedu nazvanú Konštanty, v ktorej si uložíme konštanty s názvami jednotlivých elementov v XML súbore:

public final class Konstanty {

    public static final String UZIVATELE = "uzivatele";

    public static final String UZIVATEL = "uzivatel";

    public static final String VEK = "vek";
    public static final String JMENO = "jmeno";
    public static final String REGISTROVAN = "registrovan";

}

Trieda XmlSaxCteni

Vytvorenú triedu XmlSaxCteni oddedíme od triedy org.xml.sax.helpers.DefaultHandler. Tým sa nám sprístupnia metódy, ktoré neskôr budeme potrebovať pri parsovaní súboru. K projektu pripojíme aj triedu Uzivatel. Užívateľa budeme chcieť načítať do nejakej kolekcie, vytvorme si teda prázdny list ArrayList nazvaný uzivatele.

private final List<Uzivatel> uzivatele = new ArrayList<>();

Pripravíme si pomocné premenné pre atribúty používateľa. Nemôžeme ukladať priamo do inštancie, pretože trieda nemá settery.

Druhou možnosťou môže byť settery pridať, tým ale strácame časť zapuzdrenia.

Premenné naplníme predvolenými hodnotami, tie sa dosadia v prípade, že daná hodnota nebude v XML zapísaná. Ďalej si vytvoríme premenné pre indikáciu, že spracovávame vek alebo dátum registrácie:

private String jmeno = "";
private int vek = 0;
private LocalDate registrovan = LocalDate.now();

private boolean zpracovavamJmeno = false;
private boolean zpracovavamRegistrovan = false;

Metóda parsuj()

V hlavnej triede XmlSaxCteni si založíme privátnu metódu parsuj(Path soubor), ktorá bude ako parameter prijímať cestu k XML súboru v podobe inštancie triedy Path:

private void parsuj(Path soubor) throws SAXException, IOException, ParserConfigurationException {
    // TODO tu vyplníme telo metódy
}

V tele tejto metódy odštartujeme samotné parsovanie. Na čítanie XML pomocou SAX nám Java poskytuje abstraktnú triedu SAXParser. Inštanciu tejto triedy získame pomocou metódy newSAXParser(), ktorú poskytuje továrenská trieda SAXParserFactory, ktorej inštanciu získame zavolaním SAXParseFactory.newInstance(). Nad inštanciou parseru jednoducho zavoláme metódu parse(), ktoré odovzdáme ako parametre súbor, ktorý chceme naparzovať a handler, ktorý sa o parsovanie postará.

Telo metódy teda bude vyzerať nasledovne:

private void parsuj(Path soubor) throws SAXException, IOException, ParserConfigurationException {
    // Vytvorenie inštancie parseru
    SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
    // Spustenie parsovania
    parser.parse(soubor.toFile(), this);
    // Nakoniec si používateľa vypíšeme do konzoly
    uzivatele.forEach(System.out::println);
}

Metódy startElement(), endElement() a characters()

Teraz prišiel čas prepísať metódy, ktoré nám trieda DefaultHandler ponúka. Prepíšeme celkom tri metódy: startElement(), endElement() a characters():

@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
    // Metóda sa zavolá vždy, keď parser narazí na nový element
}

@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
    // Metóda sa zavolá vždy, keď parser narazí na zatvárací element
}

@Override
public void characters(char[] ch, int start, int length) throws SAXException {
    // Metóda sa zavolá vždy, keď nám parser ponúka prečítať hodnotu medzi elementmi
}

Metóda startElement()

V metóde startElement() nás budú zaujímať predovšetkým dva parametre: qName a attributes. Prvý menovaný parameter obsahuje názov elementu, ktorý sa práve spracováva. Druhý obsahuje atribúty spracovávaného elementu. Aby sme zistili, ktorý element sa práve spracováva, použijeme jednoduchý switch:

public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
    switch (qName) {
        case Konstanty.UZIVATEL:
            // Vek používateľa získame z atribútu používateľa
            vek = Integer.parseInt(attributes.getValue(Konstanty.VEK));
            break;
        case Konstanty.JMENO:
            // Pre spracovanie mena si musíme uložiť indikátor, že práve spracovávame meno; čítanie hodnoty vykonáme inde
            zpracovavamJmeno = true;
            break;
        case Konstanty.REGISTROVAN:
            // Pre spracovanie dátumu registrácie si musíme uložiť indikátor, že práve spracovávame dátum registrácie; čítanie hodnoty vykonáme inde
            zpracovavamRegistrovan = true;
            break;
    }
}

Metóda endElement()

V metóde endElement(), ktorá sa volá na pri stretnutí s uzatváracím tagom, jednoducho prepneme príslušný indikátor späť na false:

public void endElement(String uri, String localName, String qName) throws SAXException {
    switch (qName) {
        case Konstanty.JMENO:
            // Ak sme spracovávali meno, tak prepnime indikátor mena na  false spracovavamMeno = false; break; case Konstanty.REGISTROVAN: // Ak sme spracovávali dátum registrácie, tak prepnime indikátor dátumu registrácie na false
            zpracovavamRegistrovan = false;
            break;
        case Konstanty.UZIVATEL:
            // Pokiaľ sme prečítali všetky dáta z užívateľa, vytvoríme novú inštanciu a pridáme ju do kolekcie
            Uzivatel uzivatel = new Uzivatel(jmeno, vek, registrovan);
            uzivatele.add(uzivatel);
            break;
    }
}

Metóda characters()

Poslednou metódou, ktorú ešte potrebujeme vyplniť, je metóda characters(), pomocou ktorej budeme čítať hodnotu medzi elementmi. Na zistenie, akú hodnotu práve chceme prečítať, využijeme naše indikátory. Metóda teda bude vyzerať takto:

public void characters(char[] ch, int start, int length) throws SAXException {
    // Vytvoříme novou instanci textu
    String text = new String(ch, start, length);
    if (zpracovavamJmeno) { // Pokud zpracováváme jméno, tak ho jednoduše přiřadíme
        jmeno = text;
    } else if (zpracovavamRegistrovan) { // Pokud zpracováváme datum registrace, tak ho naparsujeme
        registrovan = LocalDate.parse(text, Uzivatel.formatData);
    }
}

Pokiaľ máme veľa atribútov, ktoré musíme načítať, začne nám metóda characters() nepríjemne „pučať“. Alternatívny spôsob spracovania môže byť pomocou využitia kolekcie typu HashMap, kedy si pre spracovanie jednotlivých atribútov vytvoríme lambda funkciu, ktorú uložíme práve do kolekcie typu HashMap. Ako kľúč použijeme názov atribútu. Viac o implementácii si môžete prečítať v článku so ZIP súbormi.

Metóda main()

Nakoniec pridáme main() metódu, kde vytvoríme novú inštanciu a spustíme parsovanie:

public static void main(String[] args) {
    Path soubor = Paths.get(System.getProperty("user.home"), "itnetwork", "soubor.xml");
    try {
        new XmlSaxCteni().parsuj(soubor);
    } catch (SAXException | IOException | ParserConfigurationException e) {
        e.printStackTrace();
    }
}

Testovanie

Výsledkom spusteného kódu budú tri načítané mená zo súboru:

Konzolová aplikácia
Pavel Slavík, 22, 21.March 2000
Jan Novák, 31, 30.October 2012
Tomáš Marný, 16, 1.January 2011

Pokiaľ sa vám načítanie príliš nepáčilo, dám vám za pravdu. Zatiaľ čo generovanie nového XML súboru je SAXom veľmi jednoduché a prirodzené, načítanie je naozaj krkolomné.

V budúcej lekcii Čítanie a zápis XML súborov pomocou DOM v Jave , sa pozrieme na DOM, teda objektový prístup k XML dokumentu.


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 496x (36.98 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Java

 

Predchádzajúci článok
Zápis XML súborov SAXom v Jave
Všetky články v sekcii
Práca so súbormi v Jave
Preskočiť článok
(neodporúčame)
Čítanie a zápis XML súborov pomocou DOM v Jave
Článok pre vás napísal David Hartinger
Avatar
Užívateľské hodnotenie:
1 hlasov
David je zakladatelem ITnetwork a programování se profesionálně věnuje 15 let. Má rád Nirvanu, nemovitosti a svobodu podnikání.
Unicorn university David sa informačné technológie naučil na Unicorn University - prestížnej súkromnej vysokej škole IT a ekonómie.
Aktivity