Usando XML em Java com SAX e XStream
Veja neste artigo um review geral sobre XML e como usá-lo (escrita e leitura) em Java com SAX e XStream.
Com a quantidade imensa de linguagens de programação disponíveis atualmente no mercado, faz-se necessário criar uma ponte de comunicação comum entre estas, uma forma de comunicação comum que independa da linguagem e que torne essa comunicação possível.
Um caso comum seria a comunicação de um Sistema Bancário desenvolvido em Java com o sistema de uma empresa de entrega de encomendas desenvolvido em .NET, onde o sistema bancário enviaria a remessa de cartões que devem ser entregues e a empresa de entrega irá se encarregar de realizar essa tarefa. Existem várias formas de concretizar essa comunicação entre linguagens distintas, e algumas delas são:
- Através de um arquivo XML;
- Através de um WebService;
- Através de requisições HTTP;
- Através de arquivo de texto puro.
Essas são apenas quatro formas possíveis e muito utilizadas para comunicação entre sistemas distintos, onde cada uma tem sua peculiaridade. No caso do WebService, os métodos do outro sistema são acessados diretamente, com o HTTP são enviados parâmetros via requisição HTTP, geralmente por POST e o texto puro é o método menos organizado para que isso seja feito, pois não possui nenhuma definição padrão, a não ser a definida pela própria empresa.
Neste artigo estudaremos o XML em Java e veremos como funciona a sua estrutura, como criá-lo e como fazer a leitura em Java.
Entendendo o XML
O XML foi formalizado pela W3C afim de padronizar a estrutura de um documento para que todo tipo de linguagem seja capaz de entender, tecnicamente falando, uma linguagem de marcação.
Alguns dos princípios básicos do XML, regidos pela W3C são:
- Separação de conteúdo da formatação;
- Simplicidade e legibilidade;
- Possibilidade de criação de tags novas;
- Criação de arquivos para validação (DTDs e schemas).
A vantagem em usar o XML está no fato de que a interpretação do mesmo é comum a todos, independente da linguagem utilizada. E a maior desvantagem está no uso de existirem formatos mais simples como JSON, por exemplo, pois mesmo o XML parecendo simples para os mais experientes, ainda há formatos mais simples ainda. Pois bem, vamos nos ater as funcionalidades provenientes do XML, como mostra a Listagem 1.
<?xml version="1.0" encoding="ISO-8859-1"?>
<receita nome="pão" tempo_de_preparo="5 minutos" tempo_de_cozimento="1 hora">
<titulo>Pão simples</titulo>
<ingredientes>
<ingrediente quantidade="3" unidade="xícaras">Farinha</ingrediente>
<ingrediente quantidade="7" unidade="gramas">Fermento</ingrediente>
<ingrediente quantidade="1.5" unidade="xícaras" estado="morna">Água</ingrediente>
<ingrediente quantidade="1" unidade="colheres de chá">Sal</ingrediente>
</ingredientes>
<instrucoes>
<passo>Misture todos os ingredientes, e dissolva bem.</passo>
<passo>Cubra com um pano e deixe por uma hora em um local morno.</passo>
<passo>Misture novamente, coloque numa bandeja e asse num forno.</passo>
</instrucoes>
</receita>
O cabeçalho acima “” é padrão de todo XML e logo abaixo temos a descrição de nossas próprias tags e seus valores. Veja que ele segue uma estrutura hierárquica de pai-filho:
A tag 'receita' é a nossa “tag pai” e possui internamente as tags titulo, ingredientes e instruções. A tag ingredientes possui a tag ingrediente e a tag instruções possui a tag passo. Temos então a seguinte ordem:
- “ receita → titulo, ingredientes, instrucoes”
- “ingredientes → ingrediente
- “instrucoes → passo”
Poderíamos ter duas receitas no mesmo XML, bastando que a outra tag receita seja aberta no mesmo nível hierárquico da primeira tag receita. O mesmo poderia ocorrer com as outras, como titulo, instruções, ingredientes, passo e etc.
XML em Java
Há pelo menos três formas de realizar a leitura de XML em Java:
- Utilizar regex do pacote java.util.regex e criar seu próprio algoritmo para realizar a leitura de XML, o que é bem pouco prático e um tarefa nada trivial;
- Usando a API SAX, que veremos mais adiante;
- Usando o Xstream que também veremos mais adiante;
Usando SAX para ler XML em Java
O SAX é uma API para ler dados em XML e funciona como um Parser, pois ele analisa a estrutura de dados contida no XML. Este lê de forma sequencial não permitindo leituras aleatórias ou voltar a um trecho já lido, e a cada lido de um novo elemento é disparado um evento que veremos mais a seguir.
A vantagem da leitura sequencial é que torna o processo mais rápido, visto que não há necessidade de conhecer todo documento, apenas o trecho que está sendo lido. O SAX é perfeito quando estamos trabalhando com a leitura de pequenos trechos de XML e não há necessidade realizar leituras aleatórias.
O SAX dispara um evento a cada trecho lido, são eles: no início da leitura do documento, no final da leitura do documento, no início de um elemento, no fim do elemento e na leitura dos valores.
Para que isso seja possível temos que criar uma classe que estenda de DefaultHandler e que poderá sobreescrever os métodos desejados com a lógica que desejamos executar. Para entender como funciona o Handler, vamos primeiro mostrar o MyHandler, de acordo com a Listagem 2 e depois detalharemos método a método.
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
public class MyHandler extends DefaultHandler {
private StringBuffer estruturaLida = new StringBuffer(200);
private StringBuffer valorAtual = new StringBuffer(100);
public void startDocument() {
System.out.print("Iniciando leitura XML ...");
}
public void endDocument() {
System.out.print("\n Finalizando leitura XML...");
}
public void startElement(
String uri,
String localName,
String tag,
Attributes atributos) {
estruturaLida.append("/" + tag);
System.out.print(
"\n<"
+ estruturaLida.substring(1)
+ (atributos.getLength() != 0 ? " +ATRIBUTOS" : "")
+ ">");
valorAtual.delete(0, valorAtual.length());
}
public void endElement(String uri, String localName, String tag) {
System.out.print(valorAtual.toString().trim());
valorAtual.delete(0, valorAtual.length());
estruturaLida.delete(
estruturaLida.length() - tag.length() - 1,
estruturaLida.length());
}
public void characters(char[] ch, int start, int length) {
valorAtual.append(ch, start, length);
}
}
A Listagem 2 possui um Handler completo que será usado pelo SAX para monitorar os eventos ocorridos no decorrer da leitura do nosso XML. Após a leitura da Listagem 2 e localização dos métodos, nós explicaremos nas listagens abaixo cada um destes métodos e seu objetivo.
Veja que temos dois atributos: estruturaLida e valorAtual. A estruturaLida irá armazenar tudo que foi lido até o momento no XML, enquanto que o valorAtual irá armazenar sempre o valor que está sendo lido.
Optamos por usar StringBuffer por seu desempenho oferecido e não usamos a String por ser uma classe imutável, ou seja, seu valor não pode ser alterado ao contrário do que muitos pensam. Isso significa que ao concatenar um valor em uma String na verdade você está criando uma nova String em memória, mesmo que esteja atribuindo o resultado para ela mesma. Isso ocorre porque Strings são imutáveis. Fique atento a isso para não cometer este erro.
public void startDocument() {
System.out.print("Iniciando leitura XML ...");
}
O método startDocument() da Listagem 3 é um dos eventos que são chamados é no início da leitura do XML, aqui poderíamos colocar qualquer lógica que desejássemos quando a leitura do XML começasse. Por exemplo, suponha que na leitura de algum novo XML devêssemos enviar um Email notificando alguém sobre essa leitura, poderíamos colocar essa lógica no startDocument().
public void endDocument() {
System.out.print("\n Finalizando leitura XML...");
}
Assim como temos evento disparado no início da leitura do XML, temos o eventos disparado no final da leitura do XML, como o apresentado na Listagem 4. Poderíamos colocar lógicas que serão executadas quando a leitura terminar como, por exemplo, uma flag indicando se um determinado XML foi lido.
Para XML's extensos, esses dois eventos podem ser muito importantes, pois poderíamos evitar concorrência de leituras colocando uma flag no início da leitura indicando que o XML já está sendo lido e uma flag no final da leitura indicando que o XML já foi lido, assim evitamos que dois leitores possam tentar abrir o mesmo arquivo.
public void startElement(
String uri,
String localName,
String tag,
Attributes atributos) {
estruturaLida.append("/" + tag);
System.out.print(
"\n<"
+ estruturaLida.substring(1)
+ (atributos.getLength() != 0 ? " +ATRIBUTOS" : "")
+ ">");
valorAtual.delete(0, valorAtual.length());
}
O startElement() da Listagem 5 é chamado no início de cada elemento. Na Listagem 1, por exemplo, o início de um elemento poderia ser o <receita nome=”pão”...>. Isso significa que no início de cada elemento este evento será chamado.
A variável tag armazena o nome da tag de início do elemento e, neste exemplo que demos, seria o valor “receita”. A cada início de um novo elemento nós adicionamos dentro da variável estruturaLida e logo em seguida mostramos a estruturaLida até o momento com seus atributos, se houverem. Os atributos representados pela classe Attributes correspondem as propriedades das tags. Por exemplo, na Listagem 1 os atributos da tag receita são 'nome', 'tempo_de_preparo' e 'tempo_de_cozimento'.
public void characters(char[] ch, int start, int length) {
valorAtual.append(ch, start, length);
}
O evento da Listagem 6 é chamado na leitura dos valores que as tags possuem. Por exemplo, na Listagem 1 o valor da tag titulo é “Pão Simples”. A cada chamada deste evento nós colocamos o valor da tag dentro da variável valorAtual, que será mostrado na finalização da tag, ou seja, no método endDocument().
Agora iremos aplicar nosso MyHandler em um SAX e fazer a leitura do XML mostrado na Listagem 1, como mostra a Listagem 7.
import java.io.IOException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class SAXMain {
/**
* @param args
*/
public static void main(String[] args) {
try {
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
InputSource input = new InputSource("/home/ronaldo/Documentos/receita.xml");
parser.parse(input, new MyHandler());
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
O primeiro passo para usar o SAX é criá-lo usando o Factory SAXParserFactory. Com essa classe nós retornamos uma nova instância do objeto SAXParser e já podemos utilizá-lo para leitura do XML. O objeto “parser” possui um método chamado “parser()” que recebe um InputSource e um DefaultHandler, onde o InputSource faz referência ao arquivo XML que iremos ler, e o DefaultHandler pode ser o nosso MyHandler que irá manipular os eventos de leitura de XML.
Ao usar o “new InputSource()” nós dizemos onde está localizado nosso XML e passamos este objeto ao método parser() logo abaixo. Quando o método parser() é chamado, a leitura é iniciada e os eventos começam a serem disparados. Veja na Listagem 8 o resultado da Listagem 7 usando nosso receita.xml que é a Listagem 1.
Iniciando leitura XML ...
<receita +ATRIBUTOS>
<receita/titulo>Pão simples
<receita/ingredientes>
<receita/ingredientes/ingrediente +ATRIBUTOS>Farinha
<receita/ingredientes/ingrediente +ATRIBUTOS>Fermento
<receita/ingredientes/ingrediente +ATRIBUTOS>Ãgua
<receita/ingredientes/ingrediente +ATRIBUTOS>Sal
<receita/instrucoes>
<receita/instrucoes/passo>Misture todos os ingredientes, e dissolva bem.
<receita/instrucoes/passo>Cubra com um pano e deixe por uma hora em um local morno.
<receita/instrucoes/passo>Misture novamente, coloque numa bandeja e asse num forno.
Finalizando leitura XML...
Usando Xstream
Uma alternativa para o SAX é o Xstream, perfeito para trabalhar com beans e persistência de dados, tornando essa tarefa muito mais simples e você já entenderá o porque.
Você deve ter percebido que o SAX é simples e possibilita uma infinidade de soluções, até mesmo a escrita e leitura de beans, mas essa tarefa pode ser nada trivial quando tratamos de beans com propriedades complexas que possuem outros beans internos, ou seja, beans dentro de beans. O Xstream está aqui para resolver este problema.
Em nosso caso queremos pegar um bean qualquer, com propriedades simples e queremos transformá-lo em um XML, como mostra a Listagem 9.
import java.util.Date;
public class Cliente {
private String nome;
private int codigo;
private Date dataNascimento;
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public int getCodigo() {
return codigo;
}
public void setCodigo(int codigo) {
this.codigo = codigo;
}
public Date getDataNascimento() {
return dataNascimento;
}
public void setDataNascimento(Date dataNascimento) {
this.dataNascimento = dataNascimento;
}
}
O bean acima é simples e contém apenas propriedades simples para que possamos ver de forma rápida e fácil o que o Xstream irá criar.
import java.util.Date;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
public class XStreamMain {
public static void main (String args[]){
Cliente cliente = new Cliente();
cliente.setCodigo(123);
cliente.setDataNascimento(new Date());
cliente.setNome("RONALDO LANHELLAS");
XStream stream = new XStream(new DomDriver());
System.out.println(stream.toXML(cliente));
}
}
Vamos entender o que foi feito na Listagem 10: criamos nosso objeto Cliente e configuramos todos os seus valores, depois criamos um objeto Xstream passando um outro objeto DomDriver, que não é nosso foco agora entrar a fundo sobre o DomDriver, mas precisamos saber que deveremos passar o driver desejado para fazer a geração/leitura do nosso XML com as seguintes possibilidades disponíveis: SAX, DOM, DOM4J e outros. Cada um possui sua peculiaridade, consumindo mais ou menos memória, sendo mais rápido ou mais lento e etc.
O importante é notar o quão simples é criar o Xstream e usando o método toXML() passamos o objeto cliente que será convertido automaticamente para uma String em XML. Veja o nosso resultado na Listagem 11.
<Cliente>
<nome>RONALDO LANHELLAS</nome>
<codigo>123</codigo>
<dataNascimento>2015-01-16 01:52:16.666 UTC</dataNascimento>
</Cliente>
Assim como podemos converter um objeto para XML, podemos fazer o inverso através do método fromXML(). Por isso, logo no início dissemos que o Xstream é ótimo para quem deseja trabalhar com conversões de beans entre XML. Se você fosse implementar a mesma funcionalidade no SAX, com certeza levaria um bom tempo, afinal estamos falando aqui de qualquer bean e não apenas um bean específico.
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
public class XStreamMain {
public static void main (String args[]){
String XML = "<Cliente>"+
"<nome>RONALDO LANHELLAS</nome>"+
"<codigo>123</codigo>"+
"<dataNascimento>2015-01-16 01:52:16.666 UTC</dataNascimento>"+
"</Cliente>";
XStream stream = new XStream(new DomDriver());
Cliente cliente = (Cliente) stream.fromXML(XML);
System.out.println(cliente.getCodigo()+" | "+cliente.getNome()+" |
"+cliente.getDataNascimento());
}
}
Resultado:
123 | RONALDO LANHELLAS | Thu Jan 15 22:52:16 BRT 2015
Na Listagem 12 temos o processo inverso do que foi mostrado no método toXML(), convertemos um XML (String) em um objeto Cliente, que poderia ser qualquer outro objeto e não apenas o Cliente. Isso é muito comum na Nota Fiscal Eletrônica, por exemplo, pois como já é de praxe, só pode ser enviado XML para validação de notas fiscais e o Xstream pode ser um aliado muito útil nestas horas.
O Xstream não é uma biblioteca padrão do JDK e para utilizá-lo você terá que importar um arquivo jar. Basta que você coloque o mesmo no classpath do seu projeto e comece a usar o Xstream.
O StAX é uma evolução do SAX com algumas melhorias e ele não precisa de Handler. Ele usa uma técnica conhecida por Pull Parser, onde o cliente quem pega os dados do StAX e não o Handler como é no SAX, que usa a técnica Push Parsing.
Vimos neste artigo um review geral sobre XML quais as formas de utilizá-lo em Java com os recursos: SAX e Xstream. Ambos são ótimos em seus cenários, sendo o SAX para leituras rápidas de XML pequenos e o Xstream quando estamos trabalhando com beans. Um objeto de NotaFiscal poderia facilmente ser convertido para XML para fins legais usando o Xstream.
O quão útil é cada recurso em um cenário específico é você quem vai analisar. Por isso, é muito importante o entendimento de ambos para poder aplicá-los de forma correta. Apresentamos também um recurso conhecido como StAX para leitura/escrita de XML.
Artigos relacionados
-
Artigo
-
Artigo
-
Artigo
-
Artigo
-
Vídeo