Lekce 7 - Zápis XML souborů SAXem v Javě
V předchozím kvízu, Kvíz - Práce s CSV soubory a úvod do XML v Javě, jsme si ověřili nabyté zkušenosti z předchozích lekcí.
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 609x (4.24 kB)
Aplikace je včetně zdrojových kódů v jazyce Java