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
