Vianoce v ITnetwork sú tu! Dobí si teraz kredity a získaj až 80 % extra kreditov na e-learningové kurzy ZADARMO. Zisti viac.
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í.

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 XmlSaxReading.

Súbor file.xml

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

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<users>
    <user age="22">
        <name>John Smith</name>
        <registered>3/21/2000</registered>
    </user>
    <user age="31">
        <name>James Brown</name>
        <registered>10/30/2012</registered>
    </user>
    <user age="16">
        <name>Tom Hanks</name>
        <registered>1/1/2011</registered>
    </user>
</users>

Trieda User

A tu je naša trieda User:

public class User {

    private String name;
    private int age;
    private LocalDate registered;
    public static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("M/d/yyyy");

    public User(String name, int age, LocalDate registered) {
        this.name = name;
        this.age = age;
        this.registered = registered;
    }

    @Override
    public String toString() {
        return String.format("%s, %d, %s", name, age, dateTimeFormatter.format(registered));
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public LocalDate getRegistered() {
        return registered;
    }

}

Trieda Constants

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

public final class Constants {

    public static final String USERS = "users";

    public static final String USER = "user";

    public static final String AGE = "age";
    public static final String NAME = "name";
    public static final String REGISTERED = "registered";

}

Trieda XmlSaxReading

Vytvorenú triedu XmlSaxReading 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 User. Užívateľa budeme chcieť načítať do nejakej kolekcie, vytvorme si teda prázdny list ArrayList nazvaný users.

private final List<User> users = 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 name = "";
private int age = 0;
private LocalDate registered = LocalDate.now();

private boolean processingName = false;
private boolean processingRegistered = false;

Metóda parse()

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

private void parse(Path file) throws SAXException, IOException, ParserConfigurationException {
    // TODO implement the method body
}

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 parse(Path file) throws SAXException, IOException, ParserConfigurationException {
    // Create of a instance of a parser
    SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
    // Start parsing
    parser.parse(file.toFile(), this);
    // Print the users to the console
    users.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 {
    // This method is called every time the parser finds a new element
}

@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
    // This method is called every time the parser finds a closing tag
}

@Override
public void characters(char[] ch, int start, int length) throws SAXException {
    // This method is called every time the parser offers us to read the element value
}

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 Constants.USER:
            // We'll get the user's age from the attribute
            age = Integer.parseInt(attributes.getValue(Constants.AGE));
            break;
        case Constants.NAME:
            // To process the name, we need to store an information we're processing it since we'll read the value at a different place
            processingName = true;
            break;
        case Constants.REGISTERED:
            // To process the registration date, we need to store a similar information
            processingRegistered = 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 Constants.NAME:
            // If we were processing the name, we'll set this indicator to false
            processingName = false;
            break;
        case Constants.REGISTERED:
            // If we were processing the registration date, we'll set this indicator to false
            processingRegistered = false;
            break;
        case Constants.USER:
            // If we've read all the user data, we can create a new user instance and add it to the collection
            User user = new User(name, age, registered);
            users.add(user);
            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 {
    // We create a new String instance
    String text = new String(ch, start, length);
    if (processingName) { // When processing the name, we simple assign it
        name = text;
    } else if (processingRegistered) { // When processing the registration date, we parse it
        registered = LocalDate.parse(text, User.dateTimeFormatter);
    }
}

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 file = Paths.get(System.getProperty("user.home"), "ictdemy", "file.xml");
    try {
        new XmlSaxReading().parse(file);
    } 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
John Smith, 22, 3/21/2000
James Brown, 31, 10/30/2012
Tom Hanks, 16, 1/1/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é 0x (5.82 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