Advanced Java Services | DOM Documentbuilder |
Mit Hilfe von DOM wir die XML-Datei ihrer Struktur gemäß vollständig in den Hauptspeicher geladen und in einem Baum gespeichert. Eine javax.xml.parsers.DocumentBuilderFactory liefert einen javax.xml.parsers.DocumentBuilder mit dem man, wie der Name verrät, eine Instanz einer Klasse erstellt die das Interface org.w3c.dom.Document implementiert hat. In das anfänglich leere Document wird dann die XML-Datei eingelesen. Sodann kann man auf das XML-Document sowohl lesend als auch schreibend zugreifen und das veränderte Document wieder speichern. Der Zugriff auf einzelne Knoten ist nicht sehr komfortabel und wird erst durch das XPath-Konzept drastisch erleichtert.
Das Einlesen ist straightforward.
public static void main(String[] args) { try { DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); //dbFactory.setNamespaceAware(true); DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); // ParserConfigurationException File fXmlFile = new File("buecher.xml"); Document doc = dBuilder.parse(fXmlFile); // IOException doc.getDocumentElement().normalize(); navigate(doc); } catch (DOMException | ParserConfigurationException | SAXException | IOException ex) { ex.printStackTrace(); } }
Im folgenden wird der Baum in der Methode navigate() ausgelesen und auf die Konsole ausgegeben. Eine große Hilfe ist dabei die Methode getElementsByTagName(), die uns eine Liste von Knoten (Node) liefert. Im eingelesen Baum stehen nämlich z. Bsp. auch alle Zeilenumbrüche als Textknoten. Sie erschweren das Navigieren. Deswegen gibt es drei Helpermethoden (nicht aus der API) mit denen man diese Textknoten überspringen kann.
/* * Die Methode liefert die Daten zu einem Elementnode, das nur noch ein Child hat. * Falls kein Elementnode übergeben wird, wird null zurückgegeben * Falls zwischen <xxx></xxx> keine Daten sind liefert getNodeValue() null * und in diesem Fall wird auch null zurückgegeben */ static String getChildContent(Node leaf) { Node textNode = leaf.getFirstChild(); // Es gibt nur ein Child if (textNode==null) return null; String text = textNode.getNodeValue(); if (text==null) return null; return text.trim(); }
/* * Die Methode liefert den nächsten Sibling, der ein ELEMENT_NODE ist * oder null wennn es keinen gibt */ static Node getNextElementSibling(Node node) { for(Node sibling = node.getNextSibling(); ; sibling = sibling.getNextSibling() ) { if (sibling==null) return null; //System.out.println(sibling.getNodeName()); if (sibling.getNodeType()== Node.ELEMENT_NODE) return sibling; } }
/* * Die Methode liefert zu einem übergebenen Knoten alle Kindknoten, die * Elementknoten sind. Falls es keine Kindelementknoten gibt wird null zurückgegeben */ static ArrayList<Node> getChildElementNodesAsArrayList(Node node) { NodeList childNodes = node.getChildNodes(); if (childNodes==null) return null; ArrayList<Node> elementChildNodes = new ArrayList<>(); for(int j=0; j < childNodes.getLength(); j++) { Node child = childNodes.item(j); if (child.getNodeType() == Node.ELEMENT_NODE) { elementChildNodes.add(child); } } if(elementChildNodes.isEmpty()) return null; return elementChildNodes; }
private static void navigate(Document doc) { NodeList titelNodeList = doc.getElementsByTagName("titel"); // alle <titel> for (int i = 0; i < titelNodeList.getLength(); i++) { Node titleNode = titelNodeList.item(i); System.out.println(titleNode.getNodeName()); String titelText = getChildContent(titleNode); System.out.println(titelText); // Da die Siblings auch Textnodes sein können muss man nach ELEMENT_NODE's suchen Node autorSibling = getNextElementSibling(titleNode); // autor // Bei den Childnodes ist es das gleiche Problem, btauche die ELEMENT childs NodeList childNodes = autorSibling.getChildNodes(); ArrayList<Node> elementChildNodes = getChildElementNodesAsArrayList(autorSibling); // System.out.println(elementChildNodes.size()); //NodeList elementChildNodes = getChildElementNodes(autorSibling); for (int j = 0; j < elementChildNodes.size(); j++) { // System.out.println(childElement.getNodeName()); // vorname, nachname String name = getChildContent(elementChildNodes.get(j)); System.out.print(name + " "); } System.out.println(); // jetzt braucht man den Nachbarn (Sibling) von autor Node isbnSibling = getNextElementSibling(autorSibling); // isbn NamedNodeMap attributes = isbnSibling.getAttributes(); Node item = attributes.item(0); //System.out.println(item.getNodeName()); // type System.out.print("ISBN-" + item.getNodeValue() + ": "); String isbn = getChildContent(isbnSibling); System.out.println(isbn); } }
titel Deutschland, ein Wintermaerchen Heinrich Heine ISBN-10: 3-86666-192-9 titel Der Untertan Heinrich Mann ISBN-10: 3-86677-192-9 titel Erfolg Lion Feuchtwanger ISBN-10: 3-86688-192-9
Als Beispiel fügen wir ein neues Buch ein.
static void alterDocument(Document doc) { Element buchElem = doc.createElement("buch"); Node titelChild = doc.createElement("titel"); Text bovary = doc.createTextNode("Madame Bovary"); Node autorChild = doc.createElement("autor"); Node vornameChild = doc.createElement("vorname"); Text gustave = doc.createTextNode("Gustave"); Node nachnameChild = doc.createElement("nachname"); Text flaubert = doc.createTextNode("Flaubert"); Node isbnChild = doc.createElement("isbn"); buchElem.appendChild(titelChild); titelChild.appendChild(bovary); // text buchElem.appendChild(autorChild); autorChild.appendChild(vornameChild); vornameChild.appendChild(gustave); autorChild.appendChild(nachnameChild); nachnameChild.appendChild(flaubert); buchElem.appendChild(isbnChild); Element root = doc.getDocumentElement(); root.appendChild(buchElem); }
Wir verzichten hier auf das Einfügen von Zeilenumbrüchen und machen das erst beim analogen Beispiel mit JDOM.
Das Speichern erfordert ein wenig Aufwand.
import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; //... static void writeToFile(Document doc, String filename) { try { TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); DOMSource source = new DOMSource(doc); File dest = new File(filename); StreamResult result = new StreamResult(dest); transformer.transform(source, result); } catch (TransformerFactoryConfigurationError | TransformerException ex) { ex.printStackTrace(); } }