Bienvenidos sean a este post, hoy nos centraremos como trabajar con DOM para manipular nuestros archivos XML, y como comentamos en otro post este metodo tambien tiene sus pros y contras, un pro a diferencia de trabajar con SAX es no tener la necesidad de crear un parser, esto nos permite evitar tener que hacer las clases pertinentes para poder crearlas pero los inconvenientes vienen a la hora de acceder a nuestro archivo y el otro tema es que necesita cargar todo el archivo y si este es muy grande puede generar problemas de memoria, para entender un poco mejor este concepto vamos a volver a trabajar con nuestra app Asteroides, para ello crearemos una nueva clase llamada AlmacenPuntuacionesXML_DOM y modificaremos su codigo por el siguiente:

Anuncios
package org.example.asteroides;

import android.content.Context;
import android.util.Log;

import org.w3c.dom.Document;

import java.io.FileNotFoundException;
import java.util.Vector;

public class AlmacenPuntuacionesXML_DOM implements AlmacenPuntuaciones {

    private static String ARCHIVO = "puntuaciones.xml";
    private Context contexto;
    private Document documento;
    private boolean cargadoDocumento;

    public AlmacenPuntuacionesXML_DOM(Context contexto){
        this.contexto = contexto;
        cargadoDocumento = false;
    }

    @Override
    public void guardarPuntuacion(int puntos, String nombre, long fecha){
        try{
            if (!cargadoDocumento)
                leerXML(contexto.openFileInput(ARCHIVO));
        } catch (FileNotFoundException e){
            crearXML();
        } catch (Exception e){
            Log.e("Asteroides", e.getMessage(),e);
        }
        nuevo(puntos, nombre, fecha);
        try{
            escrbirXML(contexto.openFileOutput(ARCHIVO,
                    Context.MODE_PRIVATE));
        } catch (Exception e) {
            Log.e("Asteroides", e.getMessage(),e);
        }
    }
    
    @Override
    public Vector<String> listaPuntuaciones(int cantidad){
        try{
            if (!cargadoDocumento)
                leerXML(contexto.openFileInput(ARCHIVO));
        } catch (FileNotFoundException e){
            crearXML();
        } catch (Exception e) {
            Log.e("Asteroides", e.getMessage(),e);
        }
        return aVectorString();
    }
}

En esta primera parte crearemos la base porque implementaremos a AlmacenPuntuaciones, luego crearemos cuatro variables: la primera de tipo constante llamada ARCHIVO para almacenar el nombre de nuestro archivo, luego crearemos una para el contexto, otra para el documento de tipo Document y por ultimo una de tipo boolean llamada cargadoDocumento para indicar el estado de carga del mismo, luego tendremos un constructor para recibir el contexto y asignarlo a la variable y en la misma decimos que cargadoDocumento es igual a false (falso), despues definiremos los dos metodos de la interfaz AlmacenPuntuaciones, la primera es guardarPuntuacion donde recibiran tres datos (nombre, puntos y fecha) para luego chequear si cargadoDocumento es igual a false, en caso de ser cierto procede a llamar al metodo leerXML() y le decimos cual es el archivo pertinente, no se preocupen por el error porque mas adelante crearemos este metodo, todo esto dentro de un bloque try/catch donde chequearemos dos clases de errores el primero es por si no encuentra el archivo (FileNotFoundException) y procede a crearlo por medio de crearXML, a este metodo lo crearemos mas adelante, el siguiente error es uno mas general y lo unico que hace es escribirlo en el log, una vez pasado esto llamaremos al metodo nuevo(), que lo veremos a continuacion, y por ultimo un bloque try/catch donde llamara al metodo escribirXML() para escribir todo lo creado con nuevo(), nuestro siguiente definicion sera el metodo listaPuntuaciones() de la interfaz, este caso es muy similar primero tenemos el bloque try/catch chequea que cargadoDocumento sea falso, en caso de cumplirse esta condicion llama a leerXML(), en caso de no existir el archivo lo crea por medio de crearXML, esto gracias al catch de FileNotFoundException, y en caso de otra falla lo escribe en el log, una vez pasado esto procede a devolverlo por medio del metodo aVectorString, nuestro siguiente paso sera agregar los metodos descriptos, para ello agregaremos los siguientes bloques dentro de nuestra clase:

Anuncios
    public void crearXML(){
        try{
            DocumentBuilderFactory fabrica = DocumentBuilderFactory
                    .newInstance();
            DocumentBuilder constructor = fabrica.newDocumentBuilder();
            documento = constructor.newDocument();
            Element raiz = documento.createElement("lista_puntuaciones");
            documento.appendChild(raiz);
            cargadoDocumento = true;
        } catch (Exception e){
            Log.e("Asteroides", e.getMessage(),e);
        }
    }

    public void leerXML(InputStream entrada) throws Exception {
        DocumentBuilderFactory fabrica = DocumentBuilderFactory
                .newInstance();
        DocumentBuilder constructor = fabrica.newDocumentBuilder();
        documento = constructor.parse(entrada);
        cargadoDocumento = true;
    }

    public void nuevo(int puntos, String nombre, long fecha){
        Element puntuacion = documento.createElement("puntuacion");
        puntuacion.setAttribute("fecha", String.valueOf(fecha));
        Element e_nombre = documento.createElement("nombre");
        Text texto = documento.createTextNode(nombre);
        e_nombre.appendChild(texto);
        puntuacion.appendChild(e_nombre);
        Element e_puntos = documento.createElement("puntos");
        texto = documento.createTextNode(String.valueOf(puntos));
        e_puntos.appendChild(texto);
        puntuacion.appendChild(e_puntos);
        Element raiz = documento.getDocumentElement();
        raiz.appendChild(puntuacion);
    }

    public Vector<String> aVectorString(){
        Vector<String> resultado = new Vector<String>();
        String nombre = "", puntos = "";
        Element raiz = documento.getDocumentElement();
        NodeList puntuaciones = raiz.getElementsByTagName("puntuacion");
        for(int i = 0; i < puntuaciones.getLength(); i++){
            Node puntuacion = puntuaciones.item(i);
            NodeList propiedades = puntuacion.getChildNodes();
            for(int j = 0; j < propiedades.getLength(); j++){
                Node propiedad = propiedades.item(j);
                String etiqueta = propiedad.getNodeName();
                if(etiqueta.equals("nombre")){
                    nombre = propiedad.getFirstChild().getNodeValue();
                } else if(etiqueta.equals("puntos")){
                    puntos = propiedad.getFirstChild().getNodeValue();
                }
            }
            resultado.add(nombre + " " + puntos);
        }
        return resultado;
    }    

    public void escrbirXML(OutputStream salida) throws Exception{
        TransformerFactory fabrica = TransformerFactory.newInstance();
        Transformer transformador = fabrica.newTransformer();
        transformador.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,"yes");
        transformador.setOutputProperty(OutputKeys.INDENT,"yes");
        DOMSource fuente = new DOMSource(documento);
        StreamResult resultado = new StreamResult(salida);
        transformador.transform(fuente, resultado);
    }

Hablemos sobre el primer metodo, crearXML(), este se encargara de crear nuestro archivo XML y para ello primero construiremos un objeto llamado fabrica el cual es de tipo DocumentBuilderFactory lo cual nos permitira crear una nueva instancia dandonos la posibilidad de trabajar con el mismo, luego crearemos el constructor el cual por medio de fabrica utilizara el metodo newDocumentBuilder(), para luego gracias a este conductor lo pasaremos a documento y crearemos el mismo, despues crearemos un objeto llamado raiz de tipo Element el cual creara el elemento o tag en el documento al cual llamaremos lista_puntuaciones y despues por medio de appendChild() lo agregaremos a nuestro documento y por ultimo cambiaremos a cargadoDocumento a true (verdadero), tambien implementamos un bloque try/catch para cualquier eventualidad, nuestro siguiente metodo sera el encargado de leer el documento, leerXML(), como en el caso anterior para poder trabajar con nuestro archivo deberemos crear un DocumentBuilderFactory para poder instanciarlo, luego el constructor, y despues crearemos un parse para nuestro documento y por ultimo setearemos a cargadoDocumento en true, en este caso debemos informarle cual es el archivo en cuestion y esto es gracias al atributo entrada.

Anuncios

Nuestro siguiente metodo se llama nuevo() la cual recibira tres datos (nombre, puntos y fecha) y aca tendremos un poco mas dificil la forma de crear nuestros elementos pero no tanto, primero deberemos crear el elemento de tipo Element por ejemplo para este caso primero creamos uno llamado puntuacion, luego le agregaremos un atributo (va a ser la unica ocasion) al cual le agregaremos un valor y en este caso va a ser fecha, para poder trabajar con los elementos primero deberemos crear un objeto de tipo Element, el cual sera el tag que se vera en el archivo y esto por medio del metodo createElement() y en este metodo indicaremos el nombre, luego deberemos crear un objeto de tipo Text para introducir la informacion en el tag antes creado y para ello usaremos el createTextNode() donde le pasaremos algunos de los atributos informados en el metodo, una vez hecho esto por medio de appendChild() agregaremos al objeto de tipo Element el objeto de tipo Text que hemos creado, una vez hecho esto podremos agregar este ultimo objeto de tipo Element a nuestro objeto creado en primera instancia llamado puntuacion, este ciclo se puede repetir la cantidad de veces necesarias pero como pueden observar en nuestro codigo solo lo realizamos dos veces, para el nombre y para los puntos, y una vez finalizado crearemos un objeto llamado raiz para obtener la raiz de nuestro documento y agregarle nuestro elemento creado aqui llamado puntuacion.

Nuestro siguiente metodo sera aVectorString(), el cual sera el verdadero encargado de mostrar nuestro listado de puntuaciones, para ello primero crearemos un objeto de tipo Vector llamado resultado, luego tendremos dos variables de tipo String llamadas nombre y puntos y en ambos casos la dejaremos en blanco, volveremos a crear un objeto llamado raiz para poder tener la raiz de nuestro documento, para luego crear un objeto llamado puntuaciones de tipo NodeList donde a raiz le diremos que busque el tag (elemento) llamado puntuacion. Para nuestro siguiente paso usaremos un bucle for donde primero usaremos a getLength() para obtener el limite de nuestro bucle, luego lo que hara basicamente este bucle sera primero obtener nodo por nodo ( o mejor dicho tag por tag ) donde primero obtendremos el nombre del mismo y luego por medio de equals() chequearemos si es nombre o puntos y obtendremos el respectivo valor para finalmente agregarlo dentro de resultado y luego devolvemos el valor de resultado, tengan en cuenta que Transformer puede ser utilizado desde una API con version superior o igual a 8 si planeas para debajo de este API deberias replantearte usar esta clase, antes de continuar veamos como quedo finalmente nuestro codigo:

Anuncios
package org.example.asteroides;

import android.content.Context;
import android.util.Log;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Vector;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

public class AlmacenPuntuacionesXML_DOM implements AlmacenPuntuaciones {

    private static String ARCHIVO = "puntuaciones.xml";
    private Context contexto;
    private Document documento;
    private boolean cargadoDocumento;

    public AlmacenPuntuacionesXML_DOM(Context contexto){
        this.contexto = contexto;
        cargadoDocumento = false;
    }

    @Override
    public void guardarPuntuacion(int puntos, String nombre, long fecha){
        try{
            if (!cargadoDocumento)
                leerXML(contexto.openFileInput(ARCHIVO));
        } catch (FileNotFoundException e){
            crearXML();
        } catch (Exception e){
            Log.e("Asteroides", e.getMessage(),e);
        }
        nuevo(puntos, nombre, fecha);
        try{
            escrbirXML(contexto.openFileOutput(ARCHIVO,
                    Context.MODE_PRIVATE));
        } catch (Exception e) {
            Log.e("Asteroides", e.getMessage(),e);
        }
    }

    @Override
    public Vector<String> listaPuntuaciones(int cantidad){
        try{
            if (!cargadoDocumento)
                leerXML(contexto.openFileInput(ARCHIVO));
        } catch (FileNotFoundException e){
            crearXML();
        } catch (Exception e) {
            Log.e("Asteroides", e.getMessage(),e);
        }
        return aVectorString();
    }

    public void crearXML(){
        try{
            DocumentBuilderFactory fabrica = DocumentBuilderFactory
                    .newInstance();
            DocumentBuilder constructor = fabrica.newDocumentBuilder();
            documento = constructor.newDocument();
            Element raiz = documento.createElement("lista_puntuaciones");
            documento.appendChild(raiz);
            cargadoDocumento = true;
        } catch (Exception e){
            Log.e("Asteroides", e.getMessage(),e);
        }
    }

    public void leerXML(InputStream entrada) throws Exception {
        DocumentBuilderFactory fabrica = DocumentBuilderFactory
                .newInstance();
        DocumentBuilder constructor = fabrica.newDocumentBuilder();
        documento = constructor.parse(entrada);
        cargadoDocumento = true;
    }

    public void nuevo(int puntos, String nombre, long fecha){
        Element puntuacion = documento.createElement("puntuacion");
        puntuacion.setAttribute("fecha", String.valueOf(fecha));
        Element e_nombre = documento.createElement("nombre");
        Text texto = documento.createTextNode(nombre);
        e_nombre.appendChild(texto);
        puntuacion.appendChild(e_nombre);
        Element e_puntos = documento.createElement("puntos");
        texto = documento.createTextNode(String.valueOf(puntos));
        e_puntos.appendChild(texto);
        puntuacion.appendChild(e_puntos);
        Element raiz = documento.getDocumentElement();
        raiz.appendChild(puntuacion);
    }

    public Vector<String> aVectorString(){
        Vector<String> resultado = new Vector<String>();
        String nombre = "", puntos = "";
        Element raiz = documento.getDocumentElement();
        NodeList puntuaciones = raiz.getElementsByTagName("puntuacion");
        for(int i = 0; i < puntuaciones.getLength(); i++){
            Node puntuacion = puntuaciones.item(i);
            NodeList propiedades = puntuacion.getChildNodes();
            for(int j = 0; j < propiedades.getLength(); j++){
                Node propiedad = propiedades.item(j);
                String etiqueta = propiedad.getNodeName();
                if(etiqueta.equals("nombre")){
                    nombre = propiedad.getFirstChild().getNodeValue();
                } else if(etiqueta.equals("puntos")){
                    puntos = propiedad.getFirstChild().getNodeValue();
                }
            }
            resultado.add(nombre + " " + puntos);
        }
        return resultado;
    }

    public void escrbirXML(OutputStream salida) throws Exception{
        TransformerFactory fabrica = TransformerFactory.newInstance();
        Transformer transformador = fabrica.newTransformer();
        transformador.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,"yes");
        transformador.setOutputProperty(OutputKeys.INDENT,"yes");
        DOMSource fuente = new DOMSource(documento);
        StreamResult resultado = new StreamResult(salida);
        transformador.transform(fuente, resultado);
    }
}
Anuncios

Nuestra ultima modificacion la debemos hacer en MainActivity donde reemplazaremos la linea donde lo asignamos a almacen por esta:

almacen = new AlmacenPuntuacionesXML_DOM(this);

Si lo compilamos y probamos obtendremos una salida similar a esta cuando probemos las puntuaciones

En este caso funciono igual al caso en el post anterior pero con sutiles diferencias, primero no necesitamos una clase adicional para crear nuestro parser, si observaron creamos uno para poder manejar nuestro archivo, si se quiere es un poco mas complicado porque tuvimos que hacer algunos pasos adicionales para poder crear nuestro elemento y su contenido.

Anuncios

En resumen, hoy hemos visto como manejar nuestro archivos XML por medio de un estandar que es DOM, hemos visto como se crean los documentos, la posibilidad de leerlo, la posibilidad de escribirlo, como agregar nuevos elementos y como obtenerlos, espero les haya sido util sigueme en Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Tambien podes donar

Es para mantenimiento del sitio, gracias!

$1.00