Tratamiento de ficheros XML con la API DOM de Java

31 de Octubre del 2016


Hola a todos, después de hablaros sobre cómo gestionar ficheros en Java hoy escribiré un post de cómo podemos tratar (lectura y escritura) ficheros XML a través del API DOM.

Como viene siendo habitual primero un pequeño repaso a los términos que utilizaremos a lo largo del post.

xml

XML (eXtensible Markup Language) y DOM (Document Object Model)

XML es un lenguaje de marcas muy estandarizado utilizado para almacenar e intercambiar datos de forma legible, caracterizado por estructurarse a través de etiquetas.

Ejemplo de archivo XML que utilizaremos en este post:

<concesionario>
  <coche id="1">
    <marca>Renault</marca>
    <modelo>Megane</modelo>
    <cilindrada>1.5</cilindrada>
  </coche>

  <coche id="2">
    <marca>Seat</marca>
    <modelo>León</modelo>
    <cilindrada>1.6</cilindrada>
  </coche>

  <coche id="3">
    <marca>Suzuki</marca>
    <modelo>Vitara</modelo>
    <cilindrada>1.9</cilindrada>
  </coche>
</concesionario>

Por otro lado tenemos DOM, es un modelo (independiente del lenguaje) utilizadas para el tratamiento de estos ficheros a la que podemos añadir SAX, JDOM o XOM. Este parseador genera un árbol de objetos llamados nodos que representan cada etiqueta del documento XML, estos nodos pueden ser de muchos tipos: documentos, elementos, atributos, etc, que se relacionan entre sí mediante relaciones padre-hijo.

Lectura de un archivo XML

A la hora de leer un archivo XML a través de DOM debemos instanciar una serie de clases antes de poder tratar el fichero. Primero utilizaremos la conocida clase File para cargar nuestro fichero.

File file = new File("coches.xml");

Posteriormente y ya dentro de un try/catch (para tratar los excepciones) parsearemos el fichero con estas clases: DocumentBuilderFactory, DocumentBuilder y Document.

try {
  DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
  DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
  Document doc = dBuilder.parse(file);
} catch(Exception e) {
  e.printStackTrace();
}

Una vez hecho todo esto ya podremos leer el archivo coches.xml además de usar otros métodos como los ejemplos de aquí abajo.

Método Descripción
getDocumentElement() Accede al nodo raíz del documento
normalize() Elimina nodos vacíos y combina adyacentes en caso de que los hubiera
// estos métodos podemos usarlos combinados para normalizar el archivo XML
doc.getDocumentElement().normalize();

Siguiendo dentro del try/catch podemos utilizar la clase NodeList para almacenar el elemento que le indicaremos como parámetro.

// almacenamos los nodos para luego mostrar la
// cantidad de ellos con el método getLength()
NodeList nList = doc.getElementsByTagName("coche");
System.out.println("Número de coches: " + nList.getLength());

Una vez tenemos almacenados los datos del nodo “coche” podemos leer su contenido teniendo en cuenta que este código depende de que conozcamos la estructura y etiquetas utilizadas.

for(int temp = 0; temp < nList.getLength(); temp++) {
  Node nNode = nList.item(temp);

  if(nNode.getNodeType() == Node.ELEMENT_NODE) {
    Element eElement = (Element) nNode;

    System.out.println("\nCoche id: " + eElement.getAttribute("id"));
    System.out.println("Marca: "
                + eElement.getElementsByTagName("marca").item(0).getTextContent());
    System.out.println("Modelo: "
                + eElement.getElementsByTagName("modelo").item(0).getTextContent());
    System.out.println("Cilindrada: "
                + eElement.getElementsByTagName("cilindrada").item(0).getTextContent());
  }
}

Nos mostraría por consola:

Elemento Raiz: concesionario
Número de coches: 3

Coche id: 1
Marca: Renault
Modelo: Megane
Cilindrada: 1.5

Coche id: 2
Marca: Seat
Modelo: León
Cilindrada: 1.6

Coche id: 3
Marca: Suzuki
Modelo: Vitara
Cilindrada: 1.9

Si no conocemos la estructura podemos utilizar el código de más abajo para leer el archivo XML.

NodeList nList = doc.getElementsByTagName("coche");

for (int i = 0; i < nList.getLength(); i++) {
  Node node = nList.item(i);

  if (node.getNodeType() == Node.ELEMENT_NODE) {
    Element eElement = (Element) node;

    if(eElement.hasChildNodes()) {
      NodeList nl = node.getChildNodes();
      for(int j=0; j<nl.getLength(); j++) {
        Node nd = nl.item(j);
        System.out.println(nd.getTextContent());
      }
    }
  }
}

Escritura archivo XML

Si quisiéramos escribir un archivo XML siguiendo la misma estructura del concesionario deberíamos instanciar las clases DocumentBuilderFactory, DocumentBuilder y Document, definir toda la estructura del archivo (siempre dentro de un bloque try/catch) y por último instanciar las clases TransformerFactory, Transformer, DOMSource y StreamResult para crear el archivo.

try {
  DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
  DocumentBuilder db = dbf.newDocumentBuilder();
  Document doc = db.newDocument();

  // definimos el elemento raíz del documento
  Element eRaiz = doc.createElement("concesionario");
  doc.appendChild(eRaiz);

  // definimos el nodo que contendrá los elementos
  Element eCoche = doc.createElement("coche");
  eRaiz.appendChild(eCoche);

  // atributo para el nodo coche
  Attr attr = doc.createAttribute("id");
  attr.setValue("1");
  eCoche.setAttributeNode(attr);

  // definimos cada uno de los elementos y le asignamos un valor
  Element eMarca = doc.createElement("marca");
  eMarca.appendChild(doc.createTextNode("Renault"));
  eCoche.appendChild(eMarca);

  Element eModelo = doc.createElement("modelo");
  eModelo.appendChild(doc.createTextNode("Megano"));
  eCoche.appendChild(eModelo);

  Element eCilindrada = doc.createElement("cilindrada");
  eCilindrada.appendChild(doc.createTextNode("1.5"));
  eCoche.appendChild(eCilindrada);

  // clases necesarias finalizar la creación del archivo XML
  TransformerFactory transformerFactory = TransformerFactory.newInstance();
  Transformer transformer = transformerFactory.newTransformer();
  DOMSource source = new DOMSource(doc);
  StreamResult result = new StreamResult(new File("ejercicio3.xml"));

  transformer.transform(source, result);
} catch(Exception e) {
  e.printStackTrace();
}

Un ejemplo de como podríamos almacenar los datos de cada nodo antes de escribirlos en un archivo XML podría ser a través de una clase en la que cada atributo sería uno de los elementos del nodo, con esto podríamos crear un array o Arraylist en el que cada posición fuese una instancia con los valores de cada nodo dentro de la estructura del XML.

public class Coche {
  private String marca;
  private String modelo;
  private String cilindrada;

  public Coches() {
    marca = "Renault";
    modelo = "Megane";
    cilindrada = "1.5";
  }

  public Coches(String nombre, String creador, String ciudad) {
    this.marca = nombre;
    this.modelo = creador;
    this.cilindrada = ciudad;
  }

  @Override
  public String toString() {
    return this.marca.replace(' ', '_') + " " +
           this.modelo.replace(' ', '_') + " " +
           this.cilindrada.replace(' ', '_');
  }
}

Lo instanciariamos de esta forma:

Coche coche = new Coche("Seat", "León", "1.5");

Finalizado este post tan solo queda recordar que mi correo iam@jmoral.es y mi Twitter @owniz están abiertos a todo el mundo para que podáis comentarme cualquier duda o corrección.

Un saludo. ツ

Icono XML diseñado por Freepik desde www.flaticon.com con licencia CC 3.0 BY
Comparte en →