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

7. diel - Zápis XML súborov SAXom v Kotline

V minulom dieli nášho seriálu tutoriálov pre Kotlin, v lekcii Úvod do formátu XML súborov v Kotlin , sme si popísali formát XML.

V dnešnom Kotline tutoriále si vytvoríme XML s niekoľkými užívateľmi pomocou knižnice SAX, reprezentovanej triedou XMLStreamWriter. Inštancie načítame z kolekcie ArrayList a zapíšeme do XML súboru.

Zápis XML pomocou SAX

Poďme si vytvoriť jednoduché XML, využijeme na to minule uvedený príklad s užívateľmi. Vytvoríme si nový projekt menom XmlSaxZapis a pridáme k nemu triedu Uzivatel.

Trieda Uzivatel

Kód triedy 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žívateľa evidujeme meno, vek a dátum registrácie. Následne prepisujeme metódu toString(), aby vracala meno používateľa. Nakoniec pripájame formátovanie dátumu.

Ukladanie do súboru

XMLStreamWriter vytvárame pomocou triedy XMLOutputFactory. Do XML môžeme uložiť samozrejme aj len jeden objekt (napr. nastavenie), my si tu ukážeme uloženie zoznamu niekoľkých objektov.

Ďalší kód budeme pre jednoduchosť písať do metódy main(). Iba si vyskúšame funkčnosť knižnice SAX. Z minulých dielov vieme, ako sa aplikácia píše správne objektovo.

Najprv si nachystáme súbor, kam budeme objekty ukladať:

// 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)
}

Kolekcia používateľov

Ďalej si vytvoríme testovaciu kolekciu ArrayList užívateľov:

// 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žitie triedy XMLStreamWriter

Teraz vytvoríme inštanciu triedy XMLStreamWriter pomocou XMLOutputFactory, tá samotná sa vytvára továrenskou metódou newInstance(). Žiaľ, nemôžeme použiť blok try-with-resources, pretože ho XMLStreamWriter nepodporuje. Inštanciu ako parameter odovzdá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žiť try-with-resources trochu krkolomný, neskôr si ukážeme aj ďalšie prístupy ku XML dokumentu.

Zápis užívateľov

Poďme sa pustiť do samotného zápisu. Najprv zapíšeme hlavičku dokumentu:

xsw.writeStartDocument()

Ďalej musí nasledovať koreňový element, v ktorom je celý zvyšok XML obsiahnutý. Na zapisovanie elementov máme metódy writeStartElement() a writeEndElement(). Prvý berie v atribúte názov elementu, ktorý otvárame. Druhá metóda spozná názov otvoreného elementu sama z kontextu dokumentu a parametre teda nemá. Otvoríme koreňový element, v našom prípade element uzivatele:

xsw.writeStartElement("uzivatele")

Teraz sa dostávame k zápisu jednotlivých používateľov, ten bude prebiehať vo foreach cykle.

Zápis hodnoty do elementu vykonáme pomocou metódy writeCharacters(), kedy parametrom je zapisovaná hodnota. Podobne môžeme elementu pridať atribút metódou writeAttribute(), ktorej parametre sú názov atribútu a jeho hodnota.

Hodnota je vždy typu String, čiže v našom prípade musíme vek na typ String previesť.

Cyklus a zápis elementu uzivatel (zatiaľ ešte bez vnorených elementov) bude teda vyzerať takto:

for (u in uzivatele) {
    xsw.writeStartElement("uzivatel")
    xsw.writeAttribute("vek", u.vek.toString())
    xsw.writeEndElement()
}

Do programu pripíšeme ešte jeden EndElement na uzavretie koreňového elementu a EndDocument na ukončenie celého dokumentu. Podobne, ako pri textových súboroch, musíme aj tu vyprázdniť buffer metódou flush().

Celý kód teda teraz vyzerá 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 skúsime spustiť a uistíme sa, že všetko funguje. Obsah súboru bude vyzerať takto:

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

Dáta vyzerajú v poriadku, ale formátovanie nie je žiadne. Poďme to napraviť.

Formátovanie XML

Pod hlavnú metódu main() vytvoríme novú metódu formatuj(). Tá bude v parametri prijímať cestu k súboru, ktorý má naformátovať:

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

Do tela metódy pridáme nasledujúce riadky:

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 koniec metódy main() pridáme volanie metódy 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)
}

Skontrolujeme, že sa nám zápis správne sformá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 spoznal, že v elemente uzivatel nie je okrem atribútu žiadna hodnota a tak tag vyrenderoval ako nepárový.

Doplnenie atribútov užívateľov

Teraz vložíme do elementu uzivatel dva ďalšie elementy, presnejšie jeho atribúty meno a dátum registrácie.

A kód zápisu v cykle sa zmení 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 miesta zápisu elementu uzivatel, teda medzi jeho writeAttribute() a writeEndElement().

A máme hotovo. Výsledný súbor vyzerá, ako sme chceli:

<?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 na stiahnutie pod článkom.

V nasledujúcej lekcii, Čítanie XML súborov SAXom v Kotline , si ukážeme, ako môžeme pomocou SAXu XML súbory čítať a vytvárať potom inštancie užívateľov, ktoré uložíme do kolekcie.


 

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é 2x (6.92 MB)
Aplikácia je vrátane zdrojových kódov v jazyku Kotlin

 

Predchádzajúci článok
Úvod do formátu XML súborov v Kotlin
Všetky články v sekcii
Súbory a práce s nimi v Kotlin
Preskočiť článok
(neodporúčame)
Čítanie XML súborov SAXom v Kotline
Článok pre vás napísal Filip Studený
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
.
Aktivity