Parsear XML usando Java Stax Cursor

Parsear documento con información meteorológica en formato XML con Java usando Stax Cursor

En esta entrada voy a realizar un sencillo ejemplo para parsear un archivo XML usando Java Stax. El archivo a parsear contiene información meteorológica de la ciudad de Sevilla. Voy a obtener los días en los que la temperatura máxima fuese mayor a una temperatura "X".

Un fragmento del archivo XML que vaa ser parseado es el siguiente:

<?xml version="1.0" encoding="ISO-8859-15"?>
<root id="41091" version="1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.aemet.es/xsd/localidades.xsd">
    <origen>
        <productor>AEMET</productor>
        <web>http://www.aemet.es</web>
        <enlace>http://www.aemet.es/es/eltiempo/prediccion/municipios/sevilla-id41091</enlace>
        <language>es</language>
        <copyright>© AEMET. Autorizado el uso de la información y su reproducción citando a AEMET como autora de la misma.</copyright>
        <nota_legal>http://www.aemet.es/es/nota_legal</nota_legal>
    </origen>
    <elaborado>2014-11-06T11:41:02</elaborado>
    <nombre>Sevilla</nombre>
    <provincia>Sevilla</provincia>
    <prediccion>
        <dia fecha="2014-11-10">
            <prob_precipitacion>25</prob_precipitacion>
            <cota_nieve_prov/>
            <estado_cielo descripcion="Intervalos nubosos">13</estado_cielo>
            <viento>
                <direccion>C</direccion>
                <velocidad>0</velocidad>
            </viento>
            <racha_max/>
            <temperatura>
                <maxima>17</maxima>
                <minima>7</minima>
            </temperatura>
            <sens_termica>
                <maxima>17</maxima>
                <minima>6</minima>
            </sens_termica>
            <humedad_relativa>
                <maxima>85</maxima>
                <minima>50</minima>
            </humedad_relativa>
            <uv_max>3</uv_max>
        </dia>
        <dia fecha="2014-11-11">
            <prob_precipitacion>100</prob_precipitacion>
            <cota_nieve_prov/>
            <estado_cielo descripcion="Cubierto con lluvia">26</estado_cielo>
            <viento>
                <direccion>SO</direccion>
                <velocidad>10</velocidad>
            </viento>
            <racha_max/>
            <temperatura>
                <maxima>18</maxima>
                <minima>10</minima>
            </temperatura>
            <sens_termica>
                <maxima>18</maxima>
                <minima>10</minima>
            </sens_termica>
            <humedad_relativa>
                <maxima>95</maxima>
                <minima>80</minima>
            </humedad_relativa>
        </dia>
    </prediccion>
</root>

La clase modelo contiene dos atributos: private String dia y private int temperatura. (Para hacerlo corto, no formateo la fecha).

public class Pojo {

    private String dia;
    private int temperatura;
    
    public Pojo() {}
    public Pojo(String dia, int temperatura) {
        this.dia = dia;
        this.temperatura = temperatura;
    }
    
    public String getDia() {
        return dia;
    }
    public void setDia(String dia) {
        this.dia = dia;
    }
    public int getTemperatura() {
        return temperatura;
    }
    public void setTemperatura(int temperatura) {
        this.temperatura = temperatura;
    }
    
    @Override
    public String toString() {
        return "Dia: " +getDia()+" Temperatura máxima: " +getTemperatura();
    }
}

El archivo Test.java ejecuta el programa. Contiene el método main, y dos métodos más. Uno genera una lista de fechas y sus tempearturas máximas de todos los días. El otro método, a partir de una lista de días obtiene aquellos días que cumplan la condición de temperatura máxima.

El procedimiento para llevarlo a cabo es:

  1. Llegar a la etiqueta de apertura dia y obtener el atributo fecha.
  2. Obtener la etiqueta maxima que está dentro de la etiqueta temperatura.
  3. Al llegar al cierre de la etiqueta dia, añado el día a la lista días.
  4. A partir de la lista generada de días, construyo método para que a partir de la lista y de una temperatura se muestren los días filtrados.

El código es sencillo y está comentado, y se puede descargar más abajo.

public class Test {

    /**
     * @param args
     */
    public static void main(String[] args) {
        String fileName;
        fileName = "src/inventadoAemet/localidad_41091.xml";
        List<Pojo> dias = parseXML(fileName);
        
        System.out.println(dias);
        
        List<Pojo> diasfiltrados = filtroTMax(dias, 21.0);
        System.out.println(diasfiltrados);      
    }

    private static List<Pojo> parseXML(String fileName) {
        //Se devolverá lista con los días que contenga el XML.
        //Al cerrar etiquetada "día" se añade el objeto a la lista
        List<Pojo> dias = new LinkedList<Pojo>();
        
        //Objeto que se añadirá a la lista.
        //Cada iteración sobre etiqueta de apertura "día" instancia el objeto
        Pojo dia = null;
        
        //Variables que almacenan la ruta dentro de la jerarquía de etiquetas
        String path         = ""; //Raiz. Se irá variando su contenido
        String pathDia      = "/root/prediccion/dia";
        String pathTemMax   = "/root/prediccion/dia/temperatura/maxima";
        
        XMLInputFactory xmlif = XMLInputFactory.newInstance();
        try {
            XMLStreamReader xmlsr = xmlif.createXMLStreamReader(new FileInputStream(fileName));
            int event = xmlsr.getEventType();
            
            while (xmlsr.hasNext()) {
                event = xmlsr.next();
                switch(event){
                    //Se trata de etiqueta de apertura
                    case XMLStreamConstants.START_ELEMENT:
                        //Añade a Path la etiqueta apertura actual
                        path = path +"/"+ xmlsr.getLocalName();
                        //Si la ruta actual coincide con la Path
                        if (path.equalsIgnoreCase(pathDia)) {
                            dia = new Pojo();
                            //Al objeto se añade atributo de la posición "0"
                            dia.setDia(xmlsr.getAttributeValue(0));
                        }
                        break;
                    //Se trata de contenido de una etiqueta
                    case XMLStreamConstants.CHARACTERS:
                        //Si la ruta actual coincide con la Path
                        if (path.equalsIgnoreCase(pathTemMax))
                            dia.setTemperatura(Integer.parseInt(xmlsr.getText()));
                        break;
                    case XMLStreamConstants.END_ELEMENT:
                        //Si la ruta actual coincide con la Path
                        if (path.equalsIgnoreCase(pathDia))
                            dias.add(dia);//Se añade el día creado a la lista
                        //Elimina a Path la etiqueta cierre actual
                        path = path.substring(0, path.lastIndexOf("/"));
                        break;
                }               
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (XMLStreamException e) {
            e.printStackTrace();
        }
        //Devuelve la lista
        return dias;
    }
    
    /**
     * Devuelve una lista de objetos Días (Pojo) en los que la temperatura
     * temperatura máxima sea mayor a la indicada por parámetro
     * @param dias      Lista de todos los días que contiene el XML
     * @param limite    Temperatura a partir de la que se desea filtrar
     * @return          Lista de días que complen condición de temperatura
     *                  máxima mayor a la indicada
     */
    public static List<Pojo> filtroTMax(List<Pojo> dias, double limite){
        List<Pojo> filtroTempMax = new LinkedList<Pojo>();

        for (Pojo pojo : dias) {
            if (pojo.getTemperatura() > limite)
                filtroTempMax.add(pojo);
        }
        return filtroTempMax;
    }
}

El vídeo completo:

El código para descargar Xml parseado con Java Stax