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