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 Kotlin

V minulém dílu našeho seriálu tutoriálů pro Kotlin, v lekci Úvod do formátu XML souborů v Kotlin, jsme si popsali formát XML.

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

Zápis XML pomocí SAX

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 přidáme k němu třídu Uzivatel.

Třída Uzivatel

Kód třídy Uzivatel bude jednoduchý:

class Uzivatel(val jmeno: String, var vek: Int, val registrovan: LocalDate) {

    override fun toString(): String {
        return jmeno
    }

    companion object {
        var formatData = DateTimeFormatter.ofPattern("d'.'MMMM yyyy")!!
    }
}

U uživatele evidujeme jméno, věk a datum registrace. Následně přepisujeme metodu toString(), aby vracela jméno uživatele. Nakonec připojujeme formátování data.

Ukládání do souboru

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

Další 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ě.

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

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

Kolekce uživatelů

Dále si vytvoříme testovací kolekci ArrayList uživatelů:

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

Použití třídy XMLStreamWriter

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:

// zápis uživatelů
val xof = XMLOutputFactory.newInstance()
var xsw: XMLStreamWriter? = null
try {
    xsw = xof.createXMLStreamWriter(FileWriter(soubor.toString(), StandardCharsets.UTF_8))

} catch (e: IOException) {
            println("Chyba při zápisu: " + e.message)
} catch (e: XMLStreamException) {
            println("Chyba při zápisu: " + e.message)
} finally {
            try {
                if (xsw != null) {
                    xsw.close()
                }
            } catch (e: XMLStreamException) {
                println("Chyba při uzavírání souboru: " +  e.message)
            }
        }

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

Zápis uživatelů

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 probíhat ve foreach cyklu.

Zápis hodnoty do elementu provedeme pomocí metody writeCharacters(), kdy 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 (u in uzivatele) {
    xsw.writeStartElement("uzivatel")
    xsw.writeAttribute("vek", u.vek.toString())
    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 tedy nyní vypadá takto:

// zápis uživatelů
val xof = XMLOutputFactory.newInstance()
var xsw: XMLStreamWriter? = null
try {
    xsw = xof.createXMLStreamWriter(FileWriter(soubor.toString(), StandardCharsets.UTF_8))
    xsw.writeStartDocument()
    xsw.writeStartElement("uzivatele")
    for (u in uzivatele) {
        xsw.writeStartElement("uzivatel")
        xsw.writeAttribute("vek", u.vek.toString())
        xsw.writeEndElement()
        }
        xsw.writeEndElement()
        xsw.writeEndDocument()
        xsw.flush()
        } catch (e: IOException) {
            println("Chyba při zápisu: " + e.message)
        } catch (e: XMLStreamException) {
            println("Chyba při zápisu: " + e.message)
        } finally {
            try {
                xsw?.close()
            } catch (e: XMLStreamException) {
                println("Chyba při uzavírání souboru: " +  e.message)
            }
    }

Program si zkusíme spustit a ujistíme se, že vše funguje. Obsah souboru bude 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.

Formátování XML

Pod hlavní metodu main() vytvoříme novou metodu formatuj(). Ta bude v parametru přijímat cestu k souboru, který má zformátovat:

@Throws(IOException::class, ParserConfigurationException::class, TransformerException::class, SAXException::class)
private fun formatuj(soubor: String) {
    // sem napíšeme tělo formátovače
}

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

val factory = DocumentBuilderFactory.newInstance()
val builder = factory.newDocumentBuilder()
val document: Document = builder.parse("file:///$soubor")

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

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

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

Zkontrolujeme, že se nám zápis správně zformátoval:

<?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ý.

Doplnění atributů uživatelů

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 změní na:

xsw.writeStartElement("uzivatel")
xsw.writeAttribute("vek", u.vek.toString())
xsw.writeStartElement("jmeno")
xsw.writeCharacters(u.jmeno)
xsw.writeEndElement()
xsw.writeStartElement("registrovan")
xsw.writeCharacters(Uzivatel.formatData.format(u.registrovan))
xsw.writeEndElement()
xsw.writeEndElement()

Kód vložíme do místa zápisu elementu uzivatel, tedy mezi jeho writeAttribute() a writeEndElement().

A máme hotovo. Výsledný soubor vypadá, jak jsme chtěli:

<?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 ke stažení pod článkem.

V příští lekci, Čtení XML souborů SAXem v Kotlin, si ukážeme, jak můžeme pomocí SAXu XML soubory číst a vytvářet pak instance uživatelů, které uložíme do kolekce.


 

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 2x (6.92 MB)
Aplikace je včetně zdrojových kódů v jazyce Kotlin

 

Předchozí článek
Úvod do formátu XML souborů v Kotlin
Všechny články v sekci
Soubory a práce s nimi v Kotlin
Přeskočit článek
(nedoporučujeme)
Čtení XML souborů SAXem v Kotlin
Článek pro vás napsal Filip Studený
Avatar
Uživatelské hodnocení:
2 hlasů
.
Aktivity