6. XML dokumentumok feldolgozása, kezelése
6.2.2 XML dokumentum kezelése DOM segítségével
A DOM segítségével az XML dokumentumot egy faszerkezetben reprezen-táljuk, ahol minden egyes csomópont az XML egy összetevőjének felel meg. A két legagyakoribb csomópont az elem csomópont (element node) és a szöveges csomópont (text node). A DOM függvények lehetővé teszik csomópontok hoz-záadását, eltávolítását, a csomópontok tartalmának megváltoztatását és a hie-rarchia bejárását.
A DOM egy nyelvfüggetlen eszköz, ugyanis a különböző programozási nyelvekben ugyanúgy történik a használata. A DOM mechanizmusa az alábbi ábrán látható:
11. ábra: A DOM API
Itt is egy gyártó objektumot használunk, ez a DocumentBuilderFactory, amelynek feladata egy DocumentBuilder példány előállítása, ez fog létrehozni egy olyan Document objektumot, amely megfelel a DOM specifikációnak. A DocumentBuilder.newDocument metódus segítségével tudunk egy üres Document példányt létrehozni, de a parse metódussal lehetőség van egy létező XML alapján Document előállítá-sára is. A DOM API használatához szükséges osztályok és interfészek az org.w3c.dom és javax.xml.parsers csomagokban találhatóak. Nézzük mindezt a gyakorlatban.
A feldolgozás hasonlóan indul, mint a SAX esetén. Szükség van egy DocumentBuilderFactory-ra, amelynek segítségével majd előállítjuk a DocumentBuilder-ünket, ez pedig létrehozza a dokumentumfát (Document).
1 DocumentBuilderFactory
dbf=DocumentBuilderFactory.newInstance();
2 DocumentBuilder db=dbf.newDocumentBuilder();
3 Document doc=db.parse(new File("albumok.xml"));
Innentől kezdve már csak az a dolgunk, hogy feldolgozzuk a Document példányunkat. A DOM fát bejárni nem olyan egyszerű, mint a SAX-os megjelení-tés. A következő ábrán jól láthatóak a DOM elemek (Node) közötti viszonyok:
12. ábra: Az albumok.xml DOM fája a DOM kapcsolatokkal Kell írnunk egy olyan metódust a megjelenítéshez, amely végigmegy a DOM fa csomópontjain, és megjeleníti az elemek attribútumainak értékét, illet-ve az elemek értékét. A csomópontokért Javában a Node interfész felelős.
Számos csomópont típus van, és mindhez tartozik egy nodeName, nodeValue és attributes információ. Ezek a következőek lehetnek a csomópont típusától függően (csak a legfontosabbak vanna felsorolva):
Csomópont típusa nodeName nodeValue attributes ATTRIBUTE_NODE attribútum neve az attrib. értéke null
CDATA_SECTION_NODE #cdata-section a CDATA rész tartalma
null
COMMENT_NODE #comment a megjegyzés
tartalma
null
DOCUMENT_NODE #document null null
DOCUMENT_TYPE_NODE dokumentumtípus neve
null null
ELEMENT_NODE a címke neve null NamedNodeMap
ENTITY_NODE az entitás neve null null
NOTATION_NODE a jelölés neve null null
TEXT_NODE #text a text csomópont
tartalma
null
16. A legfontosabb DOM csomópontok
A Node interfész fontosabb metódusai:
String getNodeName(): a nodeName értéket adja vissza
String getNodeValue(): a nodeValue értéket adja vissza
void setNodeValue(String nodeValue): a nodeValue ér-tékének beállítása
short getNodeType(): a csomópont típusát azonosító short ér-ték (1-12)
Node getParentNode(): a csomópont szülőjét adja vissza
NodeList getChildNodes(): a csomópont gyermekeinek listája
Node getFirstChild(): a csomópont első gyermeke
Node getLastChild(): a csomópont utolsó gyermeke
Node getNextSibling(): a csomópontot követő csomópontot adja vissza
NamedNodeMap getAttributes(): a csomópont attribútumai-nak listája
Node insertBefore(Node uj,Node e): az uj csomópontot szúrja be az e elé
Node replaceChild(Node uj,Node cs): a cs csomópontot cseréli le az uj-ra
Node removeChild(Node torlendo): törli a csomópontot
Node appendChild(Node uj): az uj csomopontot rakja be az aktuális csomópont gyermek csomópontjai után
boolean hasChildNodes(): true, ha van gyermek csomópontja A DOM fa bejárását a következő metódus végzi:
1 public static void printNode(Node n) {
2 System.out.println(n.getNodeName()+" : "+
n.getFirstChild().getNodeValue());
3 if (n.getNodeType()==Node.ELEMENT_NODE) { 4 NamedNodeMap attr=n.getAttributes();
5 for (int j=0;j<attr.getLength();j++) { 6 printNode(attr.item(j));
7 }
8 NodeList nodes = n.getChildNodes();
9 for (int i=0;i<nodes.getLength();i++){
10 if (nodes.item(i).getNodeType()==
Node.ELEMENT_NODE) {
11 printNode(nodes.item(i)); csomó-pont értékét. Itt figyelni kell arra (az ábrán is látszik), hogy az elemeknél az ér-ték egy Text csomóponton belül van, ezt a Text csomópontot kérjük le a getFirstChild metódussal, és ennek az értéke lesz az elem valódi szöveges tartalma.
Ha a csomópont elem, akkor lekérjük az attribútumait, és mindre meghív-juk a printNode metódust. Majd lekérjük az elem csomópont gyermek csomó-pontjait, és ha azok is elemek, akkor azokra is meghívjuk a printNode metó-dust. Így elérhető a DOM fa teljes bejárása, legalábbis az adott feladatra (albumok.xml) vonatkozóan. A printNode első hívása így fog kinézni:
printNode(doc.getFirstChild());
Amit eddig láttunk, azt sokkal könnyebben megoldhattuk volna SAX segít-ségével. A DOM nem is bejáráskor hasznos, hanem ha módosítani kell az XML tartalmat, új elemeket, attribútumokat hozzáadni, törölni vagy módosítani.
A következő feladat az, hogy az összes album árát rakjuk be az album elemhez attribútumként, majd töröljük az ar elemet. Vagyis egy attribútum létrehozását és egy elem törlését kell elvégezni. Továbbá minden album elem-hez hozzá kell adni egy „eladott” nevű elemet, amelyben egy 100-as érték lesz (a próba kedvéért).
1 public static void setNewAttrib(Document d) { 2 NodeList nodes=d.getElementsByTagName("album");
3 NodeList prices=d.getElementsByTagName("ar");
4 for (int i=0;i<nodes.getLength();i++) { 5 if (nodes.item(i) instanceof Element) {
14 for (int i=prices.getLength()-1;i>=0;i--) {
15 prices.item(i).getParentNode().removeChild(
prices.item(i));
16 } 17 }
A metódusnak egy Document példányt adunk át, amely lényegében az XML dokumentum gyökere. Ezt kell használnunk, ha adott nevű elemeket kell lekérni a dokumentumfából vagy új elemeket kell hozzáadni ahhoz. Lekérjük az album és az ar elemeket egy NodeList-be, majd végigmegyünk rajtuk egy for ciklussal, és az i-edik albumnak beállítunk egy ar attribútumot a megfele-lő árral. Majd a Document példány (d) segítségével létrehozunk egy eladott nevű elemet (e) és egy Text típusú csomópontot (t), amelyet hozzáfűzünk az e-hez, majd ezt adjuk hozzá az aktuális album elemhez.
Ezután töröljük az összes ar elemet. Azért kell a lista végéről indulni (i=prices.getLength()-1), mert csak így törli az összes elemet. Ugyanis ellenkező esetben egy elem törlésével az utána lévők „lejjebb” csúsznak, és így minden második albumból nem fogja törölni az ar elemet!
Már csak két dolog van hátra, hogy szinte mindent tudjunk a DOM-os XML kezeléssel kapcsolatban, egy hibakezelő hozzáadása és az érvényességellenőr-zés. A hibakezeléshez írnunk kell egy olyan osztályt (pl. DOMErrorHandler), amely megvalósítja az ErrorHandler (org.xml.sax.ErrorHandler) interfészt. Érdekesség, hogy ugyanarról az interfészről van szó, amelyet a SAX-os hibakezelésnél is használtunk. A DOM feldolgozásnak ugyan köze nincs a SAX-hoz, de a hibakezelés módja azonos, vagyis sok dolgunk nincsen, a korábbi hibakezelő metódusokat kell egy osztályba belerakni:
1 public class DOMErrorHandler implements ErrorHandler{
2 ...
3 }
A hibakeresés beállítása pedig így néz ki:
1 Writer logF=new FileWriter(new Fi-le("log.txt"),true);
2 PrintWriter logS=new PrintWriter(new PrintStream(System.out));
3 db.setErrorHandler(new DOMErrorHandler(logF));
A séma hozzáadása és az érvényességellenőrzés teljesen megegyezik a SAX-nál látottakkal:
1 ...
2 SchemaFactory sf=SchemaFactory.newInstance(
"http://www.w3.org/2001/XMLSchema");
3 dbf.setSchema(sf.newSchema(new Fi-le("albumok.xsd")));
4 DocumentBuilder db=dbf.newDocumentBuilder();