Exemplo de Parser Java SAX

Um analisador SAX em Java fornece uma API para analisar documentos XML. O analisador SAX é diferente do analisador de DOM porque não carrega o XML completo na memória e lê o documento XML sequencialmente.

Analisador SAX

javax.xml.parsers.SAXParser fornece método para analisar documento XML usando manipuladores de eventos. Esta classe implementa a interface XMLReader e fornece versões sobrecarregadas dos métodos parse() para ler documentos XML a partir de File, InputStream, SAX InputSource e URI de String. A análise real é feita pela classe Handler. Precisamos criar nossa própria classe de manipulador para analisar o documento XML. Devemos implementar a interface org.xml.sax.ContentHandler para criar nossas próprias classes de manipulador. Essa interface contém métodos de retorno de chamada que recebem notificações quando ocorre um evento. Por exemplo, StartDocument, EndDocument, StartElement, EndElement, CharacterData, etc. org.xml.sax.helpers.DefaultHandler fornece implementação padrão da interface ContentHandler e podemos estender esta classe para criar nosso próprio manipulador. É aconselhável estender esta classe porque podemos precisar de apenas alguns dos métodos para implementar. Estender esta classe manterá nosso código mais limpo e fácil de manter.

Exemplo de analisador SAX

Vamos agora para o programa de exemplo do analisador SAX. Explicarei diferentes recursos detalhadamente mais tarde. 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>

Então temos um arquivo XML armazenado em algum lugar no sistema de arquivos e, ao examiná-lo, podemos concluir que ele contém uma lista de Funcionários. Cada Funcionário tem o atributo id e os campos age, name, gender e role. Vamos usar o analisador SAX para analisar este XML e criar uma lista de objetos Funcionário. Aqui está o objeto Funcionário representando o elemento Funcionário do 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 criar nossa própria classe manipuladora de SAX estendendo a classe 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 armazenar objetos Funcionário
	private List empList = null;
	private Employee emp = null;
	private StringBuilder data = null;

	// Método getter para lista de funcionários
	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")) {
			// criar um novo Funcionário e colocá-lo no Mapa
			String id = attributes.getValue("id");
			// inicializar objeto Funcionário e definir atributo id
			emp = new Employee();
			emp.setId(Integer.parseInt(id));
			// inicializar lista
			if (empList == null)
				empList = new ArrayList<>();
		} else if (qName.equalsIgnoreCase("name")) {
			// definir valores booleanos para os campos, que serão usados na configuração das variáveis do Funcionário
			bName = true;
		} else if (qName.equalsIgnoreCase("age")) {
			bAge = true;
		} else if (qName.equalsIgnoreCase("gender")) {
			bGender = true;
		} else if (qName.equalsIgnoreCase("role")) {
			bRole = true;
		}
		// criar o contêiner de dados
		data = new StringBuilder();
	}

	@Override
	public void endElement(String uri, String localName, String qName) throws SAXException {
		if (bAge) {
			// elemento age, definir idade do Funcionário
			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")) {
			// adicionar objeto Funcionário à lista
			empList.add(emp);
		}
	}

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

MyHandler contém a lista do objeto Funcionário como um campo com apenas um método getter. Os objetos Funcionário estão sendo adicionados nos métodos do manipulador de eventos. Além disso, temos um campo Funcionário que será usado para criar um objeto Funcionário e, uma vez que todos os campos estejam definidos, adicioná-lo à lista de funcionários.

Métodos do analisador SAX para substituir

Os métodos importantes para substituir são startElement(), endElement() e characters(). O SAXParser começa a analisar o documento, quando qualquer elemento de início é encontrado, o método startElement() é chamado. Estamos substituindo este método para definir variáveis booleanas que serão usadas para identificar o elemento. Também estamos usando este método para criar um novo objeto Employee sempre que o elemento de início do Employee é encontrado. Verifique como o atributo id é lido aqui para definir o campo id do objeto Employee. O método characters() é chamado quando dados de caracteres são encontrados pelo SAXParser dentro de um elemento. Note que o analisador SAX pode dividir os dados em vários pedaços e chamar o método characters() várias vezes (leia a documentação do método characters() da classe ContentHandler). É por isso que estamos usando StringBuilder para manter esses dados usando o método append(). O endElement() é o lugar onde usamos os dados do StringBuilder para definir as propriedades do objeto employee e adicionar o objeto Employee à lista sempre que encontramos a tag de elemento de término do Employee. Abaixo está o programa de teste que usa MyHandler para analisar o XML acima para uma 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);
        //Obter lista de Funcionários
        List empList = handler.getEmpList();
        //imprimir informações do funcionário
        for(Employee emp : empList)
            System.out.println(emp);
    } catch (ParserConfigurationException | SAXException | IOException e) {
        e.printStackTrace();
    }
    }

}

Aqui está a saída do programa acima.

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 fornece métodos de fábrica para obter a instância do SAXParser. Estamos passando um objeto File para o método parse juntamente com a instância MyHandler para lidar com os eventos de retorno de chamada. O SAXParser é um pouco confuso no início, mas se você estiver trabalhando em um documento XML grande, ele fornece uma maneira mais eficiente de ler XML do que o DOM Parser. Isso é tudo para o SAX Parser em Java.

Você pode baixar o projeto do nosso Repositório do GitHub.

Referência: SAXParser, DefaultHandler

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