Ejemplo del Analizador SAX de Java

El analizador SAX en Java proporciona una API para analizar documentos XML. El analizador SAX es diferente al analizador DOM porque no carga el XML completo en memoria y lee el documento XML de forma secuencial.

Analizador SAX

javax.xml.parsers.SAXParser proporciona un método para analizar un documento XML utilizando controladores de eventos. Esta clase implementa la interfaz XMLReader y proporciona versiones sobrecargadas de los métodos parse() para leer el documento XML desde un archivo, InputStream, SAX InputSource y URI de cadena. El análisis real lo realiza la clase Handler. Necesitamos crear nuestra propia clase de controlador para analizar el documento XML. Necesitamos implementar la interfaz org.xml.sax.ContentHandler para crear nuestras propias clases de controlador. Esta interfaz contiene métodos de devolución de llamada que reciben notificaciones cuando ocurre un evento. Por ejemplo, StartDocument, EndDocument, StartElement, EndElement, CharacterData, etc. org.xml.sax.helpers.DefaultHandler proporciona una implementación predeterminada de la interfaz ContentHandler y podemos extender esta clase para crear nuestro propio controlador. Es recomendable extender esta clase porque es posible que solo necesitemos implementar unos pocos de los métodos. Extender esta clase mantendrá nuestro código más limpio y mantenible.

Ejemplo de analizador SAX

Vamos a pasar al programa de ejemplo del analizador SAX ahora, explicaré diferentes características en detalle más adelante. employees.xml

<?xml version="1.0" encoding="UTF-8"?>
<Employees>
	<Employee id="1">
		<age>29</age>
		<name>Pankaj</name>
		<gender>Male</gender>
		<role>Java Developer</role>
	</Employee>
	<Employee id="2">
		<age>35</age>
		<name>Lisa</name>
		<gender>Female</gender>
		<role>CEO</role>
	</Employee>
	<Employee id="3">
		<age>40</age>
		<name>Tom</name>
		<gender>Male</gender>
		<role>Manager</role>
	</Employee>
	<Employee id="4">
		<age>25</age>
		<name>Meghna</name>
		<gender>Female</gender>
		<role>Manager</role>
	</Employee>
</Employees>

Entonces tenemos un archivo XML almacenado en algún lugar del sistema de archivos y al observarlo, podemos concluir que contiene una lista de Empleados. Cada Empleado tiene el atributo id y los campos age, name, gender y role. Utilizaremos un analizador SAX para analizar este XML y crear una lista de objetos Employee. Aquí está el objeto Employee que representa el elemento Employee del XML.

package com.journaldev.xml;

public class Employee {
    private int id;
    private String name;
    private String gender;
    private int age;
    private String role;
    
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getRole() {
        return role;
    }
    public void setRole(String role) {
        this.role = role;
    }
    
    @Override
    public String toString() {
        return "Employee:: ID="+this.id+" Name=" + this.name + " Age=" + this.age + " Gender=" + this.gender +
                " Role=" + this.role;
    }
    
}

Vamos a crear nuestra propia clase Manejador de SAX que extiende la clase DefaultHandler.


package com.journaldev.xml.sax;

import java.util.ArrayList;
import java.util.List;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.journaldev.xml.Employee;

public class MyHandler extends DefaultHandler {

	// Lista para contener objetos Employee
	private List empList = null;
	private Employee emp = null;
	private StringBuilder data = null;

	// Método getter para la lista de empleados
	public List getEmpList() {
		return empList;
	}

	boolean bAge = false;
	boolean bName = false;
	boolean bGender = false;
	boolean bRole = false;

	@Override
	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {

		if (qName.equalsIgnoreCase("Employee")) {
			// crear un nuevo Empleado y ponerlo en el Mapa
			String id = attributes.getValue("id");
			// inicializar objeto Employee y establecer atributo id
			emp = new Employee();
			emp.setId(Integer.parseInt(id));
			// inicializar lista
			if (empList == null)
				empList = new ArrayList<>();
		} else if (qName.equalsIgnoreCase("name")) {
			// establecer valores booleanos para los campos, se utilizarán en la configuración de variables Employee
			bName = true;
		} else if (qName.equalsIgnoreCase("age")) {
			bAge = true;
		} else if (qName.equalsIgnoreCase("gender")) {
			bGender = true;
		} else if (qName.equalsIgnoreCase("role")) {
			bRole = true;
		}
		// crear el contenedor de datos
		data = new StringBuilder();
	}

	@Override
	public void endElement(String uri, String localName, String qName) throws SAXException {
		if (bAge) {
			// elemento age, establecer edad del Empleado
			emp.setAge(Integer.parseInt(data.toString()));
			bAge = false;
		} else if (bName) {
			emp.setName(data.toString());
			bName = false;
		} else if (bRole) {
			emp.setRole(data.toString());
			bRole = false;
		} else if (bGender) {
			emp.setGender(data.toString());
			bGender = false;
		}
		
		if (qName.equalsIgnoreCase("Employee")) {
			// añadir objeto Employee a la lista
			empList.add(emp);
		}
	}

	@Override
	public void characters(char ch[], int start, int length) throws SAXException {
		data.append(new String(ch, start, length));
	}
}

MyHandler contiene la lista del objeto Employee como un campo con un método getter solamente. Los objetos Employee se están agregando en los métodos del manejador de eventos. Además, tenemos un campo de tipo Employee que se utilizará para crear un objeto Employee y una vez que todos los campos estén establecidos, se añadirá a la lista de empleados.

Métodos a sobrescribir del analizador SAX

Los métodos importantes para anular son startElement(), endElement() y characters(). SAXParser comienza a analizar el documento, cuando se encuentra algún elemento de inicio, se llama al método startElement(). Estamos anulando este método para establecer variables booleanas que se utilizarán para identificar el elemento. También estamos utilizando este método para crear un nuevo objeto Employee cada vez que se encuentra un elemento de inicio de Employee. Comprueba cómo se lee el atributo de identificación aquí para establecer el campo id del objeto Employee. El método characters() se llama cuando se encuentran datos de caracteres por SAXParser dentro de un elemento. Tenga en cuenta que el analizador SAX puede dividir los datos en varios fragmentos y llamar al método characters() varias veces (lea la documentación del método characters() de la clase ContentHandler). Es por eso que estamos usando StringBuilder para mantener estos datos usando el método append(). El endElement() es el lugar donde usamos los datos de StringBuilder para establecer las propiedades del objeto empleado y agregar el objeto Employee a la lista cada vez que encontramos la etiqueta de elemento de fin de Employee. A continuación se muestra el programa de prueba que utiliza MyHandler para analizar el XML anterior en una lista de objetos Employee.

package com.journaldev.xml.sax;

import java.io.File;
import java.io.IOException;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.SAXException;

import com.journaldev.xml.Employee;

public class XMLParserSAX {

    public static void main(String[] args) {
    SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
    try {
        SAXParser saxParser = saxParserFactory.newSAXParser();
        MyHandler handler = new MyHandler();
        saxParser.parse(new File("/Users/pankaj/employees.xml"), handler);
        //Obtener lista de empleados
        List empList = handler.getEmpList();
        //imprimir información del empleado
        for(Employee emp : empList)
            System.out.println(emp);
    } catch (ParserConfigurationException | SAXException | IOException e) {
        e.printStackTrace();
    }
    }

}

Aquí está la salida del programa anterior.

Employee:: ID=1 Name=Pankaj Age=29 Gender=Male Role=Java Developer
Employee:: ID=2 Name=Lisa Age=35 Gender=Female Role=CEO
Employee:: ID=3 Name=Tom Age=40 Gender=Male Role=Manager
Employee:: ID=4 Name=Meghna Age=25 Gender=Female Role=Manager

SAXParserFactory proporciona métodos de fábrica para obtener la instancia de SAXParser. Estamos pasando un objeto File al método parse junto con una instancia de MyHandler para manejar los eventos de devolución de llamada. SAXParser es un poco confuso al principio, pero si está trabajando en un documento XML grande, proporciona una forma más eficiente de leer XML que DOM Parser. Eso es todo para SAX Parser en Java.

Puedes descargar el proyecto desde nuestro Repositorio de GitHub.

Referencia: SAXParser, DefaultHandler

Source:
https://www.digitalocean.com/community/tutorials/java-sax-parser-example