XML – Trabalhando com JAXB
Java Architecture for XML Binding (JAXB) é uma biblioteca Java que possibilita a conversão de arquivo XML em objetos Java ou vice versa. Também fornece ferramentas para geração de arquivos XSD e de validação de XML. Outra característica do JAXB é a possibilidade de geração de classes Java através de um arquivo XSD.
O JAXB é de fácil configuração, que é realizada através de anotações nativas a partir do JDK/JRE 6. Neste tutorial vamos ver alguns princípios básicos para salvar, ler e validar arquivos XML.
Vamos conhecer as principais anotações do JAXB:
@XmlRootElement
– essa anotação indica que o valor da classe será representado como um elemento XML principal;@XmlAccessorType
– indica que as anotações estão ou nos atributos ou nos métodos da classe;@XmlType
– indica que essa classe na verdade mapeia um tipo de informação específica;@XmlElement
– usada nos atributos ou métodos. Indica que o atributo será uma tag do xml;@XmlElementWrapper
– mapeia um objeto do tipo lista;@XmlAttribute
– mapeia o valor de um campo como atributo no xml.
1. Classe do Projeto
As classes que serão usadas no projeto estão descritas nas Listagens 1, 2 e 3. Estas classes estão anotadas com as anotações referentes ao JAXB e devem ser importadas do pacote javax.xml.bind.annotation
.
package com.mballem.tutorialArquivos.xmlJaxb;
import javax.xml.bind.annotation.*;
import java.util.Collection;
@XmlRootElement(name = "Contato")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(
name = "Contato",
propOrder = {"id", "nome", "email", "endereco", "telefones"},
namespace = "http://mballem.com/"
)
public class Contato {
@XmlElement(name = "id", required = true)
private int id;
@XmlElement(name = "nome", required = true)
private String nome;
@XmlElement(name = "email", required = true)
private String email;
@XmlElementWrapper(name = "Telefones")
@XmlElement(name = "Telefone")
private Collection; telefones;
@XmlElement(name = "Endereco", required = true)
private Endereco endereco;
//Gerar métodos get/set
@Override
public String toString() {
return "Contato{" +
"id=" + id +
", nome='" + nome + '\'' +
", email='" + email + '\'' +
", endereco=" + endereco +
", telefones=" + telefones +
'}';
}
}
package com.mballem.tutorialArquivos.xmlJaxb;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "Endereco")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(namespace = "http://mballem.com/")
public class Endereco {
@XmlElement(name = "id", required = true)
private int id;
@XmlElement(name = "logradouro", required = true)
private String logradouro;
@XmlElement(name = "bairro", required = true)
private String bairro;
@XmlElement(name = "cep", required = true)
private String cep;
@XmlElement(name = "cidade", required = true)
private String cidade;
@XmlElement(name = "complemento", required = false)
private String complemento;
@XmlElement(name = "numero", required = true)
private int numero;
//Gerar métodos get/set
return id;
}
@Override
public String toString() {
return "Endereco{" +
"id=" + id +
", logradouro='" + logradouro + '\'' +
", bairro='" + bairro + '\'' +
", cep='" + cep + '\'' +
", cidade='" + cidade + '\'' +
", complemento='" + complemento + '\'' +
", numero=" + numero +
'}';
}
}
package com.mballem.tutorialArquivos.xmlJaxb;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "Telefone")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(namespace = "http://mballem.com/")
public class Telefone {
@XmlElement(name = "id", required = true)
private int id;
@XmlElement(name = "ddd", required = true)
private int ddd;
@XmlElement(name = "numero", required = true)
private int numero;
//Gerar métodos get/set
@Override
public String toString() {
return "Telefone{" +
"id=" + id +
", ddd=" + ddd +
", numero=" + numero +
'}';
}
}
2. Estrutura do XML
A estrutura do XML gerado será conforme o XML da Figura 1. Uma tag raiz que será a tag <Contato>
, e duas tags filhas <Telefones>
e <Endereco>
. A tag <Telefones>
é uma lista de telefones, então conterá como tags filhas o número de telefones adicionados a um contato e cada tag filha será chamada de <Telefone>
.
Figura 1 – XML
3. Gerando e Lendo XML com JAXB
O processo usado para transformar um objeto Java em XML é chamado de Marshal e o inverso é chamado de Unmarshal. Para transformar um objeto em XML precisamos instanciar um objeto da classe JAXBContext
, e esse “context” é quem fornecerá o Marshaller ou um Unmarshaller. O Marshaller
e Unmarshaller
são interfaces do pacote javax.xml.bind
e responsáveis em fazer a conversão Objeto/XML ou XML/Objeto.
Na Listagem 4 vamos criar um método que irá gerar um XML e transformá-lo em uma String
, a qual será impressa no console no final do processo.
package com.mballem.tutorialArquivos.xmlJaxb;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.util.ArrayList;
import java.util.Collection;
public class TesteJaxB2 {
public static void main(String[] args) {
Endereco end = new Endereco();
end.setLogradouro("Rua Venancio Aires");
end.setNumero(654);
end.setComplemento("Ap.03A");
end.setBairro("Centro");
end.setCidade("Santa Maria");
end.setCep("97050-100");
end.setId(1);
Collection collection = new ArrayList();
Telefone f1 = new Telefone();
f1.setId(1);
f1.setDdd(55);
f1.setNumero(21210022);
collection.add(f1);
Telefone f2 = new Telefone();
f2.setId(2);
f2.setDdd(54);
f2.setNumero(91910022);
collection.add(f2);
Contato contato = new Contato();
contato.setId(1);
contato.setNome("Ana Maria");
contato.setEmail("ana.maria@email.com");
contato.setEndereco(end);
contato.setTelefones(collection);
System.out.println(new TesteJaxB2().marshal(contato));
}
/**
* Converte o objeto em uma String com estrutura XML.
* @param object objeto a ser convertido em XML.
* @return String contendo a estrutura XML.
*/
public String marshal(Object object) {
final StringWriter out = new StringWriter();
JAXBContext context = null;
try {
context = JAXBContext.newInstance(object.getClass());
marshaller = context.createMarshaller();
marshaller.setProperty(
javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT,
Boolean.TRUE
);
marshaller.marshal(object, new StreamResult(out));
} catch (PropertyException e) {
e.printStackTrace();
} catch (JAXBException e) {
e.printStackTrace();
}
return out.toString();
}
}
O resultado exibido no console será o apresentado na Listagem 5.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Contato>
<id>1</id>
<nome>Ana Maria</nome>
<email>ana.maria@email.com</email>
<Telefones>
<Telefone>
<id>1</id>
<ddd>55</ddd>
<numero>21210022</numero>
</Telefone>
<Telefone>
<id>2</id>
<ddd>54</ddd>
<numero>91910022</numero>
</Telefone>
</Telefones>
<Endereco>
<id>1</id>
<logradouro>Rua Venancio Aires</logradouro>
<bairro>Centro</bairro>
<cep>97050-100</cep>
<cidade>Santa Maria</cidade>
<complemento>Ap.03A</complemento>
<numero>654</numero>
</Endereco>
</Contato>
Para salvar o XML gerado em um arquivo XML, vamos usar o método, marshalToFile(Object object, String fileName)
da Listagem 6. Este método possui dois par metros, o primeiro é o objeto a ser convertido e o segundo é o destino e o nome do arquivo XML a ser gerado. Inclua no método main()
uma chamada ao método marshalToFile()
:
new TesteJaxB2().marshalToFile(contato, "C:\\contato-jaxb.xml");
Após executá-lo, vá até o diretório destino e confira se o arquivo foi gerado.
/**
* Converte o objeto em uma estrutura XML.
* @param object objeto a ser convertido em XML.
* @param fileName nome do arquivo XML a ser gerado.
* @return uma string com o conteudo do XML gerado.
*/
public String marshalToFile(Object object, String fileName) {
final StringWriter out = new StringWriter();
JAXBContext context = null;
try {
context = JAXBContext.newInstance(object.getClass());
marshaller = context.createMarshaller();
marshaller.setProperty(
javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT,
Boolean.TRUE
);
marshaller.marshal(object, new StreamResult(out));
} catch (PropertyException e) {
e.printStackTrace();
} catch (JAXBException e) {
e.printStackTrace();
}
Writer writer = null;
try {
writer = new FileWriter(fileName);
marshaller.marshal(object, writer);
} catch (JAXBException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null) {
writer.close();
}
} catch (Exception e) {
e.getMessage();
}
}
return out.toString();
}
Agora que sabemos transformar objetos em XML com JAXB, vamos fazer o contrário, transformar XML em objetos Java. Veja na Listagem 7 que o código é muito parecido com os anteriores, mas desta vez usamos a interface Unmarshaller
. No método main()
utilize o seguinte código para recuperar os dados do XML:
String xml =
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
"<Contato>" +
" <id>1</id>" +
" <nome>Ana Maria</nome>" +
" <email>ana.maria@email.com</email>" +
" <Endereco>" +
" <id>1</id>" +
" <logradouro>Rua Venancio Aires</logradouro>" +
" <bairro>Centro</bairro>" +
" <cep>97050-100</cep>" +
" <cidade>Santa Maria</cidade>" +
" <complemento>Ap.03A</complemento>" +
" <numero>654</numero>" +
" </Endereco>" +
" <Telefones>" +
" <Telefone>" +
" <id>1</id>" +
" <ddd>55</ddd>" +
" <numero>21210022</numero>" +
" </Telefone>" +
" <Telefone>" +
" <id>2</id>" +
" <ddd>54</ddd>" +
" <numero>91910022</numero>" +
" </Telefone>" +
" </Telefones>" +
"</Contato>";
O primeiro parametro do método unmarshal()
é o tipo de objeto que será recuperado e o segundo será uma String
com o conteúdo XML.
/**
* Converte um string com estrutura XML em um objeto.
* @param clazz classe referente ao tipo do objeto a ser retornado.
* @param stringXml string com o conteudo XML a ser convertido em objeto.
* @return retorna um novo objeto de clazz.
*/
public Object unmarshal(Class clazz, String stringXml) {
JAXBContext context = null;
try {
context = JAXBContext.newInstance(clazz);
unmarshaller = context.createUnmarshaller();
return unmarshaller.unmarshal(
new StreamSource(new StringReader(stringXml))
);
} catch (JAXBException e) {
e.printStackTrace();
}
return null;
}
Para recuperar o conteúdo de um arquivo .xml
, vamos usar o método unmarshalFromFile()
da Listagem 8. Adicione no método main()
o seguinte código:
Contato c2 = (Contato) new TesteJaxB2().unmarshalFromFile(
Contato.class,
"C:\\contato-jaxb.xml"
);
System.out.println(c2.toString());
Neste método passamos como par metro a classe do objeto que vamos recuperar e o local com o nome do arquivo.
/**
* Realiza a conversao (unmarshal) de um arquivo XML em um objeto do seu tipo.
* @param clazz classe referente ao objeto a ser criado a partir do XML.
* @param fileXml nome do arquivo XML a ser convertido em objeto.
* @return novo objeto.
*/
public Object unmarshalFromFile(Class clazz, String fileXml) {
JAXBContext context = null;
try {
context = JAXBContext.newInstance(clazz);
unmarshaller = context.createUnmarshaller();
return unmarshaller.unmarshal(
new FileInputStream(fileXml)
);
} catch (JAXBException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return null;
}
4. Trabalhando com arquivo XSD
O arquivo XSD é um schema, ele define quais são as regras que a estrutura do XML deve seguir, possibilitando a validação desse XML. Esse arquivo é importante para se saber qual o tipo de conteúdo que faz parte do XML. Ele informa por exemplo, se uma tag do arquivo possui dados do tipo String
, Double
, Integer
…, como também se o campo é obrigatório ou não, ou o tamanho mínimo e máximo da informação contidas no campo.
Para gerar um arquivo XSD a partir das suas classes Java, anotadas pelo JAXB, você deve abrir o console, ir até o diretório do seu código fonte e digitar o comando ‘schemagen’ mais os arquivos ‘.java’ que irão gerar o XSD. Veja na Listagem 9 o XSD referente ao projeto apresentado e, na Figura 2 o comando a ser executado.
Figura 2 – Comando schemagen
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" targetNamespace="http://mballem.com/"
xmlns:tns="http://mballem.com/"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:import schemaLocation="schema2.xsd"/>
<xs:complexType name="Contato">
<xs:sequence>
<xs:element name="id" type="xs:int"/>
<xs:element name="nome" type="xs:string"/>
<xs:element name="email" type="xs:string"/>
<xs:element name="Telefones" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element ref="Telefone" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element ref="Endereco"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="telefone">
<xs:sequence>
<xs:element name="id" type="xs:int"/>
<xs:element name="ddd" type="xs:int"/>
<xs:element name="numero" type="xs:int"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="endereco">
<xs:sequence>
<xs:element name="id" type="xs:int"/>
<xs:element name="logradouro" type="xs:string"/>
<xs:element name="bairro" type="xs:string"/>
<xs:element name="cep" type="xs:string"/>
<xs:element name="cidade" type="xs:string"/>
<xs:element name="complemento" type="xs:string" minOccurs="0"/>
<xs:element name="numero" type="xs:int"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
Os arquivos schema1.xsd
e schema2.xsd
serão salvos no diretório dos arquivos .java
. Também serão gerados arquivos .clas
s das classes usadas. Estes arquivos .class
podem ser deletados sem problema. Agora que temos um XSD de exemplo, ele poderá ser usado para validação do XML. Para isso, vamos criar 2 novas classes, a Validator
– Listagem 10 – que terá o método de validação e a classe ValidatorErrorHandler
– Listagem 11 – para captura de erros no processo de validação.
package com.mballem.tutorialArquivos.xmlJaxb;
import org.xml.sax.SAXException;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.util.JAXBSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import java.io.File;
import java.io.IOException;
public class Validator {
/**
* Realiza a validacao do objeto com o arquivo XSD.
* @param xsdFile nome do arquivo XSD.
* @param object objeto a ser validado.
*/
public void validator(String xsdFile, Object object) {
try {
JAXBContext jc = JAXBContext.newInstance(object.getClass());
JAXBSource source = new JAXBSource(jc, object);
SchemaFactory sf = SchemaFactory.newInstance(
XMLConstants.W3C_XML_SCHEMA_NS_URI
);
Schema schema = sf.newSchema(new File(xsdFile));
javax.xml.validation.Validator validator = schema.newValidator();
validator.setErrorHandler(new ValidatorErrorHandler());
validator.validate(source);
System.out.println("Successful validation!");
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
package com.mballem.tutorialArquivos.xmlJaxb;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
public class ValidatorErrorHandler implements ErrorHandler {
public void warning(SAXParseException exception) throws SAXException {
System.out.println("\nWARNING");
exception.printStackTrace();
}
public void error(SAXParseException exception) throws SAXException {
System.out.println("\nERROR");
exception.printStackTrace();
}
public void fatalError(SAXParseException exception) throws SAXException {
System.out.println("\nFATAL ERROR");
exception.printStackTrace();
}
}
Insira no método main()
as instruções:
String path = "C:\\TutorialArquivos\\src\\br\\mb\\tutorialArquivos\\xmlJaxb\\";
String file = "schema1.xsd";
new Validator().validator(path + file, contato);
O primeiro parametro é o diretório que contém o arquivo, seguido do nome do arquivo. O segundo parametro será o objeto que será validado pelo arquivo. Se a validação for bem sucedida, será exibida a mensagem “Successful validation!”, caso contrário, um erro será informado e você deverá adequar seu objeto as regras do arquivo XSD. Um erro pode acontecer quando, por exemplo, um campo é obrigatório mas no objeto ele não foi inserido. Ou então, quando um campo é do tipo String, mas no objeto ele está como Double, entre outros erros.
Outro recurso que poderá ser usado é o processo inverso, ao invés de gerar um XSD através das classes, gerar classes através de um arquivo XSD. Observe na Figura 3 o comando executado para tal ação. Primeiro foi criado um diretório chamado gerar_xsd
e copiado para dentro dele os arquivos schema1.xsd
e schema2.xsd
, gerados anteriormente. Entre no diretório pela linha de comando e execute o comando xjc
seguido do nome do arquivo schema1.xsd
. O parametro -p
é para a criação dos pacotes onde as classes serão geradas.
Figura 3 – Comando xjc
Além das classes Contato
, Endereco
e Telefone
, ele gera também uma classe chamada ObjectFactory
. A classe ObjectFactory
é responsável por criar uma inst ncia do JAXBElement
apropriada para o tipo de objeto a ser serializado, a qual pode ser usada para geração de XML’s. Já, para popular o objeto Java, com dados de um XML precisamos de um JAXBContext
, porém agora temos que pegar um objeto Unmarshaller
. O Unmarshaller
recebe um arquivo XML e devolve um JAXBElement
contendo um objeto populado – veja exemplo na Listagem 12.
package br.com.tutorial.jaxb;
import javax.xml.bind.*;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
public class TestJaxbXSD {
public static void main(String[] args) {
Endereco end = new Endereco();
end.setLogradouro("Rua Venancio Aires");
end.setNumero(654);
end.setComplemento("Ap.03A");
end.setBairro("Centro");
end.setCidade("Santa Maria");
end.setCep("97050-100");
end.setId(1);
Collection collection = new ArrayList();
Telefone f1 = new Telefone();
f1.setId(1);
f1.setDdd(55);
f1.setNumero(21210022);
collection.add(f1);
Telefone f2 = new Telefone();
f2.setId(2);
f2.setDdd(54);
f2.setNumero(91910022);
collection.add(f2);
Contato.Telefones telefones = new Contato.Telefones();
telefones.getTelefone().addAll(collection);
Contato contato = new Contato();
contato.setId(1);
contato.setNome("Ana Maria");
contato.setEmail("ana.maria@email.com");
contato.setEndereco(end);
contato.setTelefones(telefones);
System.out.println(contato.toString());
JAXBContext context = null;
try {
context = JAXBContext.newInstance("br.com.tutorial.jaxb");
Marshaller marshaller = context.createMarshaller();
JAXBElement element =
new ObjectFactory().createContato(contato);
marshaller.setProperty(
javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT,
Boolean.TRUE
);
marshaller.marshal(element, System.out);
} catch (JAXBException e) {
e.printStackTrace();
}
try {
JAXBContext.newInstance("br.com.tutorial.jaxb");
Unmarshaller unmarshaller = context.createUnmarshaller();
JAXBElement element =
(JAXBElement) unmarshaller.unmarshal(
new File("C:\\TutorialArquivos\\contato-jaxb.xml")
);
Contato c1 = element.getValue();
System.out.println(c1.toString());
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
Saiba mais
- Unofficial JAXB GUIDE – http://jaxb.java.net/guide/
- Java Architecture for XML Binding (JAXB) – http://www.oracle.com/technetwork/articles/javase/index-140168.html
- Manipulando Arquivo XML – Parte III: XStream
- Manipulando Arquivo XML – Parte II: JDOM
- Manipulando Arquivo XML – Parte I: API Nativa