Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

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

V minulej lekcii, Zápis XML súborov Saxe v Jave , sme si predstavili formát XML a ukázali si, ako pomocou SAXUM vytvoriť jednoduché XML. Teraz na minulý diel nadviažeme a napíšeme si proces opačný, teda načítanie XML súboru s používateľmi a zostavenie príslušnej objektovej štruktúry (listu užívateľov).

Pre úplnosť si opäť uvedieme náš XML súbor soubor.xml:

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

A naši triedu Uzivatel.java:

public class Uzivatel {
    private String jmeno;
    private int vek;
    private 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;
    }
}

Založme si nový projekt, pôjde opäť o konzolovú aplikáciu. Pomenujeme ju XmlSaxCteni a do zložky s projektom nakopírujeme náš XML súbor. Vytvorenú triedu oddědíme od triedy org.xml.sax.helpers.DefaultHandler. Tým sa nám sprístupní metódy, ktoré neskôr budeme potrebovať pri parsovanie súboru. K projektu pripojíme tiež triedu Uzivatel. Užívateľa budeme chcieť načítať do nejakej kolekcie, vytvorme si teda prázdny ArrayList uzivatele.

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

Konštanty

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

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";

}

Čítanie XML cez SAX

V hlavnej triede si založíme privátne metódu parsuj(String soubor), ktorá bude ako parameter prijímať cestu k XML súboru:

private void parsuj(String soubor) throws SAXException, IOException, ParserConfigurationException {
    // TODO zde vyplníme tělo
}

V tele tejto metódy "odštartujeme" samotnej parsovanie. Na čítanie XML cez SAX nám Java poskytuje abstraktné triedu SAXParser. Inštanciu tejto triedy získame pomocou továrne, ktorú poskytuje trieda SAXParserFactory.newInstance().newSAXParser(). Nad inštancií parsera jednoducho zavoláme metódu parse(), ktoré odovzdáme ako parametre súbor, ktorý chceme naparsovat a handler, ktorý sa o parsovanie postará. Telo metódy teda bude vyzerať nasledovne:

private void parsuj(String soubor) throws SAXException, IOException, ParserConfigurationException {
    // Vytvoření instance parseru
    SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
    // Spuštění parsování
    parser.parse(new File(soubor), this);
    // Nakonec si uživatele vypíšeme do konzole
    uzivatele.forEach(System.out::println);
}

Pripravíme si pomocné premenné pre atribúty používateľa. Nemôžeme ukladať priamo do inštancie, pretože trieda nemá setter. Druhou možnosťou môže byť Setter pridať, tým ale strácame časť zapuzdrenie. Premenné naplníme východiskovými hodnotami, tie sa dosadí 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;

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 {
    // Metoda se zavolá vždy, když parser narazí na nový element
}

@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
    // Metoda se zavolá vždy, když parser narazí na zavírací element
}

@Override
public void characters(char[] ch, int start, int length) throws SAXException {
    // Metoda se zavolá vždy, když nám parser nabízí přečíst hodnotu mezi elementy
}

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 zrovna spracováva, použijeme jednoduchý switch:

public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
    switch (qName) {
        case Konstanty.UZIVATEL:
            // Věk uživatele získáme z atributu uživatele
            vek = Integer.parseInt(attributes.getValue(Konstanty.VEK));
            break;
        case Konstanty.JMENO:
            // Pro zpracování jména si musíme uložit indikátor, že zrovna zpracováváme jméno; čtení hodnoty provedeme jinde
            zpracovavamJmeno = true;
            break;
        case Konstanty.REGISTROVAN:
            // Pro zpracování data registrace si musíme uložit indikátor, že zrovna zpracováváme datum registrace; čtení hodnoty provedeme jinde
            zpracovavamRegistrovan = true;
            break;
    }
}

EndElement ()

V metóde endElement(), ktorá sa volá na pri narazenie na uzatváracie tag, 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:
            // Pokud jsme zpracovávali jméno, tak přepněme indikátor jména na false
            zpracovavamJmeno = false;
            break;
        case Konstanty.REGISTROVAN:
            // Pokud jsme zpracovávali datum registrace, tak přepněme indikátor data registrace na false
            zpracovavamRegistrovan = false;
            break;
        case Konstanty.UZIVATEL:
            // Pokud jsme přečetli všechna data z uživatele, vytvoříme novou instanci a přidáme ji do kolekce
            Uzivatel uzivatel = new Uzivatel(jmeno, vek, registrovan);
            uzivatele.add(uzivatel);
            break;
    }
}

Characters ()

Posledný metódu, ktorú este potrebujeme vyplniť, je metóda characters(), pomocou ktorej budeme čítať hodnotu medzi elementy. 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);
    }
}

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

Tým máme parsovanie hotové. Nakoniec pridáme main() metódu, kde vytvoríme novú inštanciu a spustíme parsovanie:

public static void main(String[] args) {
    try {
        new XmlSaxCteni().parsuj("soubor.xml");
    } catch (SAXException | IOException | ParserConfigurationException e) {
        e.printStackTrace();
    }
}

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

Konzolová aplikácia
Pavel Slavík, 22, 21.březen 2000
Jan Novák, 31, 30.říjen 2012
Tomáš Marný, 16, 1.leden 2011

Ak sa vám načítanie príliš nepáčilo, dám vám za pravdu. Kým generovanie nového XML súboru je Saxe veľmi jednoduché a prirodzené, načítanie je naozaj krkolomné. Nabudúce, v lekcii Kvíz - Práca s CSV súbormi a úvod do XML v Jave , sa pozrieme na DOM, teda objektový prístup k XML dokumentu.

V nasledujúcom kvíze, Kvíz - Práca s CSV súbormi a úvod do XML v Jave, si vyskúšame nadobudnuté skúsenosti z predchádzajúcich lekcií.


 

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é 495x (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 Saxe v Jave
Všetky články v sekcii
Práca so súbormi v Jave
Preskočiť článok
(neodporúčame)
Kvíz - Práca s CSV súbormi a úvod do XML v Jave
Článok pre vás napísal David Hartinger
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
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