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