Saltar al contenido

Transformación de XML con SAX y JAXB en Java: Ejercicio práctico

Introducción:
En este post aprenderás a combinar dos poderosas herramientas de Java para el manejo de XML:

  • SAX (Simple API for XML): para leer el XML de forma eficiente mediante eventos.
  • JAXB (Java Architecture for XML Binding): para generar un nuevo XML a partir de objetos Java con anotaciones.

El objetivo del ejercicio es transformar un archivo XML que contiene información de países para que cada país incluya, dentro suyo, la lista de idiomas oficiales. Por ejemplo, el XML de salida tendrá la siguiente estructura para España:

<geografía>
    <paises>
        <pais nombre="España" habitantes="47420000">
            <idiomas_oficiales>
                <idioma>español</idioma>
            </idiomas_oficiales>
        </pais>
        ...
    </paises>
</geografía>

Desarrollo del Ejercicio:

  1. Lectura del XML con SAX:
    Se utiliza un parser SAX para leer el archivo paises.xml de forma eficiente. Se implementa un handler (la clase PaisesHandler) que se encarga de procesar cada elemento del XML. Al encontrar un elemento <pais>, se crea una instancia de la clase Pais y se extrae el atributo nombre. Para los elementos <habitantes> e <idioma>, se capturan los datos y se asignan al objeto correspondiente.
  2. Generación del nuevo XML con JAXB:
    Una vez leídos los datos, se construye un objeto de tipo Geografia, que contiene la lista de países. Cada objeto Pais incluye sus atributos y una lista de idiomas oficiales, mapeada mediante anotaciones JAXB. Finalmente, se genera el archivo paises_jaxb.xml con la estructura deseada.

Código completo del ejercicio:

package EjerciciosClase;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.bind.annotation.*;
import javax.xml.bind.*;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class de_XML_a_XML_con_SAX_y_JAXB {
    public static void main(String[] args) {
        String inputXML = "paises.xml";
        String outputXML = "paises_jaxb.xml";

        try {
            // Configuración del parser SAX para leer el XML de entrada
            SAXParserFactory factory = SAXParserFactory.newInstance();
            SAXParser saxParser = factory.newSAXParser();
            PaisesHandler handler = new PaisesHandler();
            saxParser.parse(new File(inputXML), handler);

            // Se crea el objeto Geografia con la lista de países leídos
            Geografia geografia = new Geografia();
            geografia.setPaises(handler.getPaises());

            // Configuración de JAXB para generar el XML de salida
            JAXBContext jaxbContext = JAXBContext.newInstance(Geografia.class);
            Marshaller marshaller = jaxbContext.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.marshal(geografia, new File(outputXML));

            System.out.println("Archivo generado: " + outputXML);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class PaisesHandler extends DefaultHandler {
    private List<Pais> paises = new ArrayList<>();
    private Pais paisActual;
    private StringBuilder contenido = new StringBuilder();
    private boolean enHabitantes = false;
    private boolean enIdioma = false;

    public List<Pais> getPaises() { return paises; }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        if (qName.equalsIgnoreCase("pais")) {
            paisActual = new Pais();
            paisActual.setNombre(attributes.getValue("nombre"));
        } else if (qName.equalsIgnoreCase("habitantes")) {
            enHabitantes = true;
            contenido.setLength(0);
        } else if (qName.equalsIgnoreCase("idioma")) {
            enIdioma = true;
            contenido.setLength(0);
        }
        // Se ignora el inicio de "idiomas_oficiales" ya que es solo un contenedor
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        if (enHabitantes || enIdioma) {
            contenido.append(new String(ch, start, length).trim());
        }
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        if (qName.equalsIgnoreCase("pais")) {
            paises.add(paisActual);
        } else if (qName.equalsIgnoreCase("habitantes")) {
            paisActual.setHabitantes(contenido.toString().trim());
            enHabitantes = false;
        } else if (qName.equalsIgnoreCase("idioma")) {
            // Se añade el idioma directamente a la lista del país actual
            paisActual.addIdioma(contenido.toString().replace("\"", "").trim());
            enIdioma = false;
        }
        // Se ignora el fin de "idiomas_oficiales"
    }
}

@XmlRootElement(name = "geografía")
@XmlAccessorType(XmlAccessType.FIELD)
class Geografia {
    @XmlElementWrapper(name = "paises")
    @XmlElement(name = "pais")
    private List<Pais> paises;

    public List<Pais> getPaises() { return paises; }
    public void setPaises(List<Pais> paises) { this.paises = paises; }
}

@XmlAccessorType(XmlAccessType.FIELD)
class Pais {
    @XmlAttribute(name = "nombre")
    private String nombre;

    @XmlAttribute(name = "habitantes")
    private String habitantes;

    @XmlElementWrapper(name = "idiomas_oficiales")
    @XmlElement(name = "idioma")
    private List<String> idiomas = new ArrayList<>();

    public String getNombre() { return nombre; }
    public void setNombre(String nombre) { this.nombre = nombre; }

    public String getHabitantes() { return habitantes; }
    public void setHabitantes(String habitantes) { this.habitantes = habitantes; }

    public List<String> getIdiomas() { return idiomas; }
    public void setIdiomas(List<String> idiomas) { this.idiomas = idiomas; }
    
    public void addIdioma(String idioma) {
        this.idiomas.add(idioma);
    }
}

Explicación del Código:

  • Lectura con SAX:
    Se utiliza SAXParser para procesar el archivo paises.xml. La clase PaisesHandler extiende DefaultHandler y sobreescribe los métodos startElement, characters y endElement para capturar la información de cada país y sus atributos (nombre, habitantes e idiomas).
  • Generación del XML con JAXB:
    Una vez recopilada la información, se instancia la clase Geografia y se asigna la lista de países. Mediante JAXB y las anotaciones @XmlRootElement, @XmlAccessorType, @XmlElementWrapper y @XmlElement, se genera un XML estructurado en el que cada país contiene un bloque <idiomas_oficiales> con sus respectivos <idioma>.

Conclusión:
Este ejercicio es ideal para quienes desean aprender a transformar la estructura de un XML en Java, combinando la eficiencia de SAX para el análisis y la simplicidad de JAXB para la generación de nuevos XML. ¡Pruébalo en tus proyectos y descubre lo potente que es trabajar con XML en Java!

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Información básica sobre protección de datos Ver más

  • Responsable: Tomas Gonzalez.
  • Finalidad:  Moderar los comentarios.
  • Legitimación:  Por consentimiento del interesado.
  • Destinatarios y encargados de tratamiento:  No se ceden o comunican datos a terceros para prestar este servicio.
  • Derechos: Acceder, rectificar y suprimir los datos.
  • Información Adicional: Puede consultar la información detallada en la Política de Privacidad.

error: Content is protected !!

Descubre más desde Tomás González: Formador y Desarrollador Web

Suscríbete ahora para seguir leyendo y obtener acceso al archivo completo.

Seguir leyendo

Este sitio web utiliza cookies, si necesitas más información puedes visitar nuestra política de privacidad    Ver
Privacidad