Advanced   Java   Services DOM Documentbuilder Back Next Up Home


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.


Eine XML-Datei mit DOM einlesen

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();
   }
}

Das Navigieren

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 Helpermethoden

static String getChildContent(Node leaf)
/*
 * 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();
}

static Node getNextElementSibling(Node node)
/*
 * 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;
   }
}

static ArrayList<Node> getChildElementNodesAsArrayList(Node node)
/*
 * 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;
}

Navigieren mit den Helper-Methoden

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);
   }
}

Der Output
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

Ändern des DOM-Baumes

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.


Speichern des DOM-Baumes mit Hilfe eines Transformers

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();
   }
}
Valid XHTML 1.0 Strict top Back Next Up Home