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

Lekce 7 - Zápis XML souborů SAXem v Javě

V dnešním Java tutoriálu si vytvoříme XML s několika uživateli pomocí knihovny SAX, třídou XMLStreamWriter. Instance načteme z kolekce ArrayList a zapíšeme do XML.

Zápis XML

Pojďme si vytvořit jednoduché XML, využijeme k tomu minule uvedený příklad s uživateli. Vytvoříme si nový projekt jménem XmlSaxZapis a k projektu přidáme následující třídu:

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

    public Uzivatel(String jmeno, int vek, LocalDate 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 LocalDate getRegistrovan() {
        return registrovan;
    }
}

Kód budeme pro jednoduchost psát do metody main(). Pouze si vyzkoušíme funkčnost knihovny SAX. Z minulých dílů víme, jak se aplikace píší správně objektově.

XMLStreamWriter vytváříme pomocí třídy XMLOutputFactory. Do XML můžeme uložit samozřejmě i jen jeden objekt (např. nastavení), my si zde ukážeme uložení seznamu několika objektů. Pokud budete chtít uložit objekt jen jeden, bude úprava již hračkou :)

Nejprve si nachystáme soubor, kam budeme objekty ukládat:

Path soubor = Paths.get(System.getProperty("user.home"), "itnetwork", "soubor.xml");
try {
    Files.createDirectories(soubor.getParent()); //vytvoří potřebné adresáře v případě že neexistují
} catch (IOException ex) {
    System.err.println("Chyba při vytváření potřebných adresářů: " + ex.getMessage());
}

Hned nato si vytvoříme testovací kolekci ArrayList uživatelů:

ArrayList<Uzivatel> uzivatele = new ArrayList<>();
LocalDate datum1 = LocalDate.of(2000, Month.MARCH, 21);
LocalDate datum2 = LocalDate.of(2012, Month.OCTOBER, 30);
LocalDate datum3 = LocalDate.of(2011, Month.JANUARY, 1);
uzivatele.add(new Uzivatel("Pavel Slavík", 22, datum1));
uzivatele.add(new Uzivatel("Jan Novák", 31, datum2));
uzivatele.add(new Uzivatel("Tomáš Marný", 16, datum3));

Nyní vytvoříme instanci třídy XMLStreamWriter pomocí XMLOutputFactory, ta samotná se vytváří tovární metodou newInstance(). Bohužel nemůžeme použít blok try-with-resources, protože ho XMLStreamWriter nepodporuje. Instanci jako parametr předáme FileWriter:

XMLOutputFactory xof = XMLOutputFactory.newInstance();
XMLStreamWriter xsw = null;
try {
    xsw = xof.createXMLStreamWriter(new FileWriter(soubor.toString(), StandardCharsets.UTF_8));
} catch (Exception e) {
    System.err.println("Chyba při zápisu: " + e.getMessage());
}
finally {
    try {
        if (xsw != null) {
            xsw.close();
        }
    } catch (Exception e) {
        System.err.println("Chyba při uzavírání souboru: " + e.getMessage());
    }
}

Kód je kvůli nemožnosti použít try-with-resources poněkud krkolomný, později si ukážeme i další přístupy ke XML dokumentu.

Pojďme se pustit do samotného zápisu. Nejprve zapíšeme hlavičku dokumentu:

xsw.writeStartDocument();

Dále musí následovat kořenový element, ve kterém je celý zbytek XML obsažen. K zapisování elementů máme metody writeStartElement() a writeEndElement(). První bere v atributu název elementu, který otevíráme. Druhá metoda pozná název otevřeného elementu sama z kontextu dokumentu a parametry tedy nemá. Otevřeme kořenový element, v našem případě element uzivatele:

xsw.writeStartElement("uzivatele");

Nyní se dostáváme k zápisu jednotlivých uživatelů, ten bude tedy přítomen ve foreach cyklu.

Zápis hodnoty do elementu provedeme pomocí metody writeCharacters(), parametrem je zapisovaná hodnota. Obdobně můžeme elementu přidat atribut metodou writeAttribute(), jejíž parametry jsou název atributu a jeho hodnota. Hodnota je vždy typu String, čili v našem případě musíme věk na typ String převést. Cyklus a zápis elementu uzivatel (zatím ještě bez vnořených elementů) bude tedy vypadat takto:

for (Uzivatel u : uzivatele) {
    xsw.writeStartElement("uzivatel");
    xsw.writeAttribute("vek", Integer.toString(u.getVek()));
    xsw.writeEndElement();
}

Do programu připíšeme ještě jeden EndElement k uzavření kořenového elementu a EndDocument k ukončení celého dokumentu. Podobně jako u textových souborů musíme i zde vyprázdnit buffer metodou flush().

Celý kód programu tedy nyní vypadá takto:

// testovací kolekce uživatelů
ArrayList<Uzivatel> uzivatele = new ArrayList<>();
LocalDate datum1 = LocalDate.of(2000, Month.MARCH, 21);
LocalDate datum2 = LocalDate.of(2012, Month.OCTOBER, 30);
LocalDate datum3 = LocalDate.of(2011, Month.JANUARY, 1);
uzivatele.add(new Uzivatel("Pavel Slavík", 22, datum1));
uzivatele.add(new Uzivatel("Jan Novák", 31, datum2));
uzivatele.add(new Uzivatel("Tomáš Marný", 16, datum3));

// Zápis uživatelů
XMLOutputFactory xof = XMLOutputFactory.newInstance();
XMLStreamWriter xsw = null;
try {
    xsw = xof.createXMLStreamWriter(new FileWriter(soubor.toString()));
    xsw.writeStartDocument();
    xsw.writeStartElement("uzivatele");

    for (Uzivatel u : uzivatele) {
        xsw.writeStartElement("uzivatel");
        xsw.writeAttribute("vek", Integer.toString(u.getVek()));
        xsw.writeEndElement();
    }

    xsw.writeEndElement();
    xsw.writeEndDocument();
    xsw.flush();
} catch (Exception e) {
    System.err.println("Chyba při zápisu: " + e.getMessage());
}
finally {
    try {
        if (xsw != null) {
            xsw.close();
        }
    } catch (Exception e) {
        System.err.println("Chyba při uzavírání souboru: " + e.getMessage());
    }
}

Program si zkusíme spustit a ujistíme se, že vše funguje. Obsah souboru by měl vypadat takto:

<?xml version="1.0" ?><uzivatele><uzivatel vek="22"></uzivatel><uzivatel vek="31"></uzivatel><uzivatel vek="16"</uzivatel></uzivatele>

Data vypadají v pořádku, ale formátování není žádné. Pojďme to napravit. Pod hlavní metodu main() vytvoříme novou metodu, kterou nazveme formatuj(String soubor). Ta bude v parametru přijímat cestu k souboru, který má zformátovat:

private static void formatuj(String soubor) {
    //  sem napíšeme tělo formátovače
}

Do těla metody přidáme následující řádky:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse("file:///" + soubor);

// Získáme novou instanci transformeru
Transformer xformer = TransformerFactory.newInstance().newTransformer();
// Nastavíme formátování pro XML
xformer.setOutputProperty(OutputKeys.METHOD, "xml");
// Nastavíme odsazení
xformer.setOutputProperty(OutputKeys.INDENT, "yes");
Source source = new DOMSource(document);
Result result = new StreamResult(new File(soubor));
xformer.transform(source, result);

A na konec metody main() přidáme volání metody formatuj():

try {
    formatuj(soubor.toString());
} catch (IOException | ParserConfigurationException | TransformerException | SAXException ex) {
    System.err.println("Chyba při formátování souboru: " + ex.getMessage());
}

Výsledný zformátovaný zápis by měl vypadat takto:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<uzivatele>
    <uzivatel vek="22"/>
    <uzivatel vek="31"/>
    <uzivatel vek="16"/>
</uzivatele>

Vidíme, že SAX poznal, že v elementu uzivatel není kromě atributu žádná hodnota a tak tag vyrenderoval jako nepárový. Nyní vložíme do elementu uzivatel dva další elementy, přesněji jeho atributy jméno a datum registrace.

A kód zápisu v cyklu se rozroste o:

xsw.writeStartElement("jmeno");
xsw.writeCharacters(u.getJmeno());
xsw.writeEndElement();
xsw.writeStartElement("registrovan");
xsw.writeCharacters(Uzivatel.formatData.format(u.getRegistrovan()));
xsw.writeEndElement();

Kód vložíme do místa zápisu elementu uzivatel, tedy mezi jeho writeAttribute() a writeEndElement(). Pro jistotu si ještě uveďme kompletní kód části s cyklem:

for (Uzivatel u : uzivatele) {
    xsw.writeStartElement("uzivatel");
    xsw.writeAttribute("vek", Integer.toString(u.getVek()));
    xsw.writeStartElement("jmeno");
    xsw.writeCharacters(u.getJmeno());
    xsw.writeEndElement();
    xsw.writeStartElement("registrovan");
    xsw.writeCharacters(Uzivatel.formatData.format(u.getRegistrovan()));
    xsw.writeEndElement();
    xsw.writeEndElement();
}

A máme hotovo. Výsledný soubor by měl vypadat takto:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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>

Hotový program je jako vždy ke stažení pod článkem.

Příště, Čtení XML souborů SAXem v Javě, budeme přes SAX XML číst.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 604x (4.24 kB)
Aplikace je včetně zdrojových kódů v jazyce Java

 

Předchozí článek
Kvíz - Práce s CSV soubory a úvod do XML v Javě
Všechny články v sekci
Soubory v Javě
Přeskočit článek
(nedoporučujeme)
Čtení XML souborů SAXem v Javě
Článek pro vás napsal David Hartinger
Avatar
Uživatelské hodnocení:
29 hlasů
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 se informační technologie naučil na Unicorn University - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity