7. diel - Zápis XML súborov SAXom v Jave
V predchádzajúcom kvíze, Kvíz - Práca s CSV súbormi a úvod do XML v Jave, sme si overili nadobudnuté skúsenosti z predchádzajúcich lekcií.
V dnešnom Java tutoriále si vytvoríme XML s niekoľkými užívateľmi
pomocou knižnice SAX, triedou XMLStreamWriter
. Inštancie
načítame z kolekcie ArrayList
a zapíšeme do XML.
Zápis XML
Poďme si vytvoriť jednoduché XML, využijeme na to minule uvedený
príklad s užívateľmi. Vytvoríme si nový projekt menom
XmlSaxWriting
a k projektu pridáme nasledujúcu triedu:
public class User { private String name; private int age; private LocalDate registered; public static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("M/d/yyyy"); public User(String name, int age, LocalDate registered) { this.name = name; this.age = age; this.registered = registered; } @Override public String toString() { return getName(); } public String getName() { return name; } public int getAge() { return age; } public LocalDate getRegistered() { return registered; } }
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íšu správne objektovo.
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. Pokiaľ budete
chcieť uložiť objekt len jeden, bude úprava už hračkou
Najprv si nachystáme súbor, kam budeme objekty ukladať:
Path file = Paths.get(System.getProperty("user.home"), "ictdemy", "file.xml"); try { Files.createDirectories(file.getParent()); // creates neccesary directories in case they don't exist } catch (IOException ex) { System.err.println("Error when creating necessary directories: " + ex.getMessage()); }
Hneď nato si vytvoríme testovaciu kolekciu ArrayList
užívateľov:
ArrayList<User> users = new ArrayList<>(); LocalDate date1 = LocalDate.of(2000, Month.MARCH, 21); LocalDate date2 = LocalDate.of(2012, Month.OCTOBER, 30); LocalDate date3 = LocalDate.of(2011, Month.JANUARY, 1); users.add(new User("John Smith", 22, date1)); users.add(new User("James Brown", 31, date2)); users.add(new User("Tom Hanks", 16, date3));
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
:
XMLOutputFactory xof = XMLOutputFactory.newInstance(); XMLStreamWriter xsw = null; try { xsw = xof.createXMLStreamWriter(new FileWriter(file.toString(), StandardCharsets.UTF_8)); } catch (Exception e) { System.err.println("Unable to write the file: " + e.getMessage()); } finally { try { if (xsw != null) { xsw.close(); } } catch (Exception e) { System.err.println("Unable to close the file: " + e.getMessage()); } }
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.
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
users
:
xsw.writeStartElement("users");
Teraz sa dostávame k zápisu jednotlivých užívateľov, ten bude teda
prítomný vo foreach
cykle.
Zápis hodnoty do elementu vykonáme pomocou metódy
writeCharacters()
, 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 user
(zatiaľ ešte bez vnorených elementov) bude teda vyzerať takto:
for (User u : user) { xsw.writeStartElement("user"); xsw.writeAttribute("age", Integer.toString(u.getAge())); 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 programu teda teraz vyzerá takto:
// user test collection ArrayList<User> users = new ArrayList<>(); LocalDate date1 = LocalDate.of(2000, Month.MARCH, 21); LocalDate date2 = LocalDate.of(2012, Month.OCTOBER, 30); LocalDate date3 = LocalDate.of(2011, Month.JANUARY, 1); users.add(new User("John Smith", 22, date1)); users.add(new User("James Brown", 31, date2)); users.add(new User("Tom Hanks", 16, date3)); // writing users XMLOutputFactory xof = XMLOutputFactory.newInstance(); XMLStreamWriter xsw = null; try { xsw = xof.createXMLStreamWriter(new FileWriter(file.toString(), StandardCharsets.UTF_8)); xsw.writeStartDocument(); xsw.writeStartElement("users"); for (User u : users) { xsw.writeStartElement("user"); xsw.writeAttribute("age", Integer.toString(u.getAge())); xsw.writeEndElement(); } xsw.writeEndElement(); xsw.writeEndDocument(); xsw.flush(); } catch (Exception e) { System.err.println("Unable to write the file: " + e.getMessage()); } finally { try { if (xsw != null) { xsw.close(); } } catch (Exception e) { System.err.println("Unable to close the file: " + e.getMessage()); } }
Program si skúsime spustiť a uistíme sa, že všetko funguje. Obsah súboru by mal vyzerať takto:
<?xml version="1.0" ?><users><user age="22"></user><user age="31"></user><user age="16"></user></users>
Dáta vyzerajú v poriadku, ale formátovanie nie je žiadne. Poďme to
napraviť. Pod hlavnú metódu main()
vytvoríme novú metódu,
ktorú nazveme format()
. Tá bude v parametri prijímať
cestu k súboru, ktorý má naformátovať:
private static void format(String file) { // we'll write the formatter body here }
Do tela metódy pridáme nasledujúce riadky:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse("file:///" + file); // Gets a new transformer instance Transformer xformer = TransformerFactory.newInstance().newTransformer(); // Sets XML formatting xformer.setOutputProperty(OutputKeys.METHOD, "xml"); // Sets indent xformer.setOutputProperty(OutputKeys.INDENT, "yes"); Source source = new DOMSource(document); Result result = new StreamResult(new File(file)); xformer.transform(source, result);
A na koniec metódy main()
pridáme volanie metódy
format()
:
try { format(file.toString()); } catch (IOException | ParserConfigurationException | TransformerException | SAXException ex) { System.err.println("Error formating file: " + ex.getMessage()); }
Výsledný sformátovaný zápis by mal vyzerať takto:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <users> <user age="22"/> <user age="31"/> <user age="16"/> </users>
Vidíme, že SAX spoznal, že v elemente user
nie je okrem atribútu žiadna hodnota a tak tag vyrenderoval ako nepárový.
Teraz vložíme do elementu user
dva ďalšie elementy, presnejšie
jeho atribúty meno a dátum registrácie.
A kód zápisu v cykle sa rozrastie o:
xsw.writeStartElement("name"); xsw.writeCharacters(u.getName()); xsw.writeEndElement(); xsw.writeStartElement("registered"); xsw.writeCharacters(User.dateTimeFormatter.format(u.getRegistered())); xsw.writeEndElement();
Kód vložíme do miesta zápisu elementu user
, teda medzi jeho
writeAttribute()
a writeEndElement()
. Pre istotu si
ešte uveďme kompletný kód časti s cyklom:
for (User u : users) { xsw.writeStartElement("user"); xsw.writeAttribute("age", Integer.toString(u.getAge())); xsw.writeStartElement("name"); xsw.writeCharacters(u.getName()); xsw.writeEndElement(); xsw.writeStartElement("registered"); xsw.writeCharacters(User.dateTimeFormatter.format(u.getRegistered())); xsw.writeEndElement(); xsw.writeEndElement(); }
A máme hotovo. Výsledný súbor by mal vyzerať takto:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <users> <user age="22"> <name>John Smith</name> <registered>3/21/2000</registered> </user> <user age="31"> <name>James Brown</name> <registered>10/30/2012</registered> </user> <user age="16"> <name>Tom Hanks</name> <registered>1/1/2011</registered> </user> </users>
Hotový program je ako vždy na stiahnutie pod článkom.
Nabudúce, Čítanie XML súborov SAXom v Jave, budeme cez SAX XML čítať.
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é 0x (5.41 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Java