JAXB – XML e Java de mãos dadas

Você já participou de um projeto que precisou ler um arquivo de configuração em xml? Já precisou consumir um xml e transformá-lo em objeto? O que você usou? Quem já trabalhou com xml sabe da dificuldade que podemos encontrar pelo caminho, e é esse tipo de dificuldade que a especificação Java Architecture for XML Binding ou simplesmente JAXB tenta resolver.

Imagine a seguinte situação: Precisarmos enviar os dados contidos em um objeto para um outro servidor. Temos muitas opções para fazer o envio, como por exemplo colocar essas informações em um arquivo de texto seguindo uma máscara pré-definida. Porém apenas as aplicações que conhecessem essa máscara entenderiam os dados, e perdemos portabilidade. Usando xml a situação já é outra: qualquer aplicação, independende de linguagem, entenderá os dados contidos no arquivo xml.

Antes de falarmos sobre o JAXB vamos primeiro conferir alguns conceitos:

XML
XML é uma linguagem de marcação que serve para guardar dados de uma forma estruturada. Essa estrutura é definida pelo próprio usuário ou por um schema. Um xml é um arquivo de texto puro, portanto independente de plataforma, por isso é muito utilizado para transmitir dados entre diferentes aplicações e sistemas. Exemplo:
carro.xml

<?xml version="1.0" encoding="UTF-8"?>
<carro>
  <nome>Fusca</nome>
  <portas>2</portas>
  <motoristas>
    <motorista>
      <nome>Guilherme</nome>
    </motorista>
    <motorista>
      <nome>Leonardo</nome>
    </motorista>
  </motoristas>
</carro>

XSD
XSD é o schema citado na seção anterior, ele define quais são as regras que a estrutura do xml deve seguir, possibilitando a validação desse xml. Exemplo:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="carro" type="Carro" />
  <xsd:complexType name="Carro">
    <xsd:sequence>
      <xsd:element name="nome" type="xsd:string" minOccurs="1"
        maxOccurs="1" nillable="false"/>
      <xsd:element name="portas" type="xsd:int" minOccurs="1"
        maxOccurs="1" nillable="false"/>
      <xsd:element name="motoristas" type="Motorista" minOccurs="0"
        maxOccurs="unbounded"/>
    </xsd:sequence>
  </xsd:complexType>
  <xsd:complexType name="Motorista">
    <xsd:sequence>
      <xsd:element name="nome" minOccurs="1" maxOccurs="1"
        type="xsd:string" nillable="false"/>
    </xsd:sequence>
  </xsd:complexType>
</xsd:schema>

O primeiro ponto da especificação apresenta uma ferramenta chamada Binding Compiler, cuja função é transformar um xsd em um conjunto de classes que tenham uma estrutura compatível com a estrutura do xml que esse xsd define.

No XSD de exemplo definimos a seguinte estrutura: Um elemento carro deve ter um elemento nome e um elemento motoristas (do tipo Motorista), seguindo essa ordem, primeiro nome e depois motoristas. Depois definimos o tipo Motorista que deve conter apenas um nome.

O Binding Compiler é independente da implementação do JAXB, ou seja, quem define como ele será executado é quem implementa a especificação, porém a maioria e inclusive a própria RI(Reference Implementation) cria um comando que pode ser chamado pela linha de comando do Sistema Operacional, o xjc. Por exemplo no Linux:

xjc carro.xsd -d src -p br.com.caelum

Se você já está usando o Java 6, o JAXB já vêm junto com o JDK.

Com esse comando o Binding Compiler gera três classes: Carro.java, Motorista.java e a ObjectFactory.java. As classes Carro e Motorista seguem a estrutura do xsd.

Gerando e Lendo XML
A segunda parte da especificação define o que temos que fazer para transformar objetos em xml e vice-versa. A API do JAXB é quem se responsabiliza por essas transformações.

Transformando objetos em xml
O processo de transformar um objeto em xml é chamado de Marshal. Com o JAXB para transformar um objeto em xml precisamos de um JAXBContext, esse context é quem fornecerá o Marshaller. O Marshaller é quem finalmente transforma um objeto (JAXBElement) em xml. O JAXBElement contém o objeto de verdade a ser serializado e algumas propriedades do xml. É aqui que entra a importância do ObjectFactory criado pelo Binding Compiler, ele é responsável por criar uma instância do JAXBElement apropriada para o tipo de objeto a ser serializado.

JAXBContext context = JAXBContext.newInstance("br.com.caelum");
Marshaller marshaller = context.createMarshaller();
JAXBElement<Carro> element = new ObjectFactory().createCarro(carro);
marshaller.marshal(element, System.out);

Parseando xml em objetos java
Para fazer o caminho contrário, ou seja popular um objeto java com dados de um xml também precisamos de um JAXBContext, porém agora temos que pegar um Unmarshaller. O Unmarshaller recebe um arquivo xml e devolve um JAXBElement contendo um objeto populado.

JAXBContext context = JAXBContext.newInstance("br.com.caelum");
Unmarshaller unmarshaller = context.createUnmarshaller();
JAXBElement<Carro> element = (JAXBElement<Carro>) unmarshaller.unmarshal(new File("resources/carro.xml"));
Carro carro = element.getValue();

Conclusão
O JAXB facilita muito a vida dos programadores java, fazendo o consumo e criação de xml menos trabalhosos. Essa API também fornece outros recursos como, validação, geração de schema (a partir de classes java, cria um xsd), opções para trabalhar com Namespace e etc. Comente nesse post outras oções do JAXB e outras bibliotecas que você usa no seu dia-a-dia.

38 Comentários

  1. Rafael Naufal 27/02/2008 at 11:22 #

    Ótimo post! Já usei também uma implementação open source da JAXB API, chamada JaxMe. Segue a mesma linha de manipulação de xml’s, por meio de Marshalling e UnMarshalling. XStream também é uma API interessante para serializar e deserializar objetos em XML também.

  2. Leandro Silva 27/02/2008 at 12:12 #

    Costumo usar o XStream, mas à vezes me deparo com algumas limitações. Algumas vezes já pensei em usar JAXB, mas o fato de ter que escrever um XSD e a partir dele gerar as classes, me desanimou um pouco. Mas outro dia desses, por conta de um XML fora do padrão que foi estabelecido aqui na empresa, acabei voltando a considerar o JAXB, justamente por causa do XSD.

    Bem, de qualquer forma, já tenho uns 10 serviços trabalhando com XStream.

  3. Eloy Lima 29/02/2008 at 00:58 #

    Ótimo post! À alguns dias utilizei o xstream com Alias para ler serviços do Yahoo, tanto XML quanto JSON e achei a programação muito simples.

  4. Bruno Pereira 12/03/2008 at 02:14 #

    Eu uso XStream quase sempre, principalmente em web services REST. Para a maioria dos casos o simples uso de aliases dá conta do recado. Para situações mais complicadas eu normalmente implemento converters. Ainda não me deparei com nenhum mapeamento que eu não conseguisse fazer usando converters.

    O XStream é tão fácil e flexível que eu tenho uma tremenda má vontade pra avaliar qualquer outra forma de binding 🙂

    Para implementar web services WS-* eu utilizei o Axis 2 com Axis Data Binding na maioria das vezes. Ele gera uma estrutura horripilante para fazer os mapeamentos e validações, mas o desenvolvedor não precisa tocar nisso.

    Para XML de uma maneira geral, XStream all the way 😉

  5. Luiz Gustavo 13/03/2008 at 18:28 #

    Pois é, esse negócio de gerar Object Factory não gostei não, é uma boa, mas devia ter uma versão mais simples para se fazer como é XStream.

  6. Joao Paulo 11/04/2008 at 11:46 #

    Olá.. legal o tuto, mas…

    porque eu usaria JAXB ao invés do XStream, sendo este último mais transparente e fácil??

    jopss

  7. maxwell 16/04/2008 at 12:40 #

    tb utilizo o XStream….com as anotações fica muito simples criar a estrutura do xml….

  8. Gustavo Touzo 16/05/2008 at 15:28 #

    Alguém sabe como posso pegar os feriados que possam estar dentro do projeto ?

  9. Gustavo Touzo 16/05/2008 at 15:31 #

    Alguém sabe como posso pegar os feriados que possam estar dentro do projeto ?
    meu e-mail: gtouzo@click21.com.br

  10. Paulo Suzart 03/07/2008 at 14:43 #

    Olá a todos,
    uma pequena diferença, ao menos ao fazer a implementação aqui no projeto é que ao fazer o unmarshal, fizemos o cast direto para a entidade JAXB:

    Carro carro = (Carro) un.unmarshal(new File(“resources/carro.xml”);

    Ao tentar usar o unmarshal com JAXBElement, recebemos um class cast exception.

    Abraços

  11. julio khichfy 28/08/2008 at 08:35 #

    HUMMMM
    vou ter que usar o JAXB
    valeu pelo começo!!!

  12. Gustavo Touzo 15/09/2008 at 06:16 #

    Estou tendo um erro de caracter inválido ao ler ao fazer o unmarshal de um XML que segue o esquema do Project. Alguém sabe dizer o que pode ser ?

  13. Claudson 03/10/2008 at 14:51 #

    Com o XStream eu não consigo fazer o marshal a partir de um XSD:
    “Can XStream generate classes from XSD?
    No. For this kind of work a data binding tool such as XMLBeans is appropriate.”
    http://xstream.codehaus.org/faq.html

  14. Sueli 11/11/2008 at 21:58 #

    Estou utilizando o JAXB, mas meu arquivo gera sempre com ns2: na frente, alguem sabe se tem algo para que eu tire estes ns3 que aparece na frente ex:
    001
    37754
    VENDAS
    1
    55

    Alguem sabe como tirar ?

  15. Dante 25/11/2008 at 06:33 #

    Muito bom amigo… aprendi a usar a ferramenta… fácil e rápido, eu queria mesmo eh gerar as classes apartir do XDS!

    Abraçosss

  16. Eder Ignatowicz 19/01/2009 at 06:16 #

    Muito obrigado pela ajuda.

  17. isaias 19/02/2009 at 00:07 #

    Conforme comentários de sueli, existe esse problema de ser gerado sempre :ns2 no namespace do xml, dependendo de como será lido, isso pode gerar problemas.

    A solução que achei na web pode ser uma GAMBI, segue link:
    http://www.guj.com.br/posts/list/83758.java

    Ou então um código gambiarrento que resolve o problema tbm

    // Passe o arquivo gerado por parametro e depois remova o ns2 abertura e fechamento
    public void ajustaXml(File file) throws Exception {

    FileReader reader = new FileReader(file);
    BufferedReader leitor = new BufferedReader(reader);

    leitor.read();

    String vlr = “”;
    StringBuffer vlrFile = new StringBuffer();
    String line = leitor.readLine();

    while(line != null) {
    vlrFile.append( line );
    line = leitor.readLine();
    }

    vlr = vlrFile.toString();

    // aqui acontece a gambi… temporariamente resolve o problema
    if (vlr.indexOf(“ns2:”) > -1) {
    vlr = vlr.replaceAll(“ns2:”, “”);
    }

    if (vlr.indexOf(“:ns2”) > -1) {
    vlr = vlr.replaceAll(“:ns2”, “”);
    }

    leitor.close();
    reader.close();

    FileWriter writer = new FileWriter(file);
    PrintWriter saida = new PrintWriter(writer);

    saida.print( “<” + vlr);

    writer.close();
    saida.close();
    }

  18. Zaqueu 22/05/2009 at 10:38 #

    Galera precisava elaborar um trabalho sobre o JAXB mais não tenho nem idéia do que seja isso e os arquivos que achei na internet estão todos em inglês. Alguém pode me dar uma mãozinha com um link ou algo do tipo?!

    Valew…

  19. saisso 16/06/2009 at 08:33 #

    Obrigado pelo post

  20. dcbasso 28/07/2009 at 11:29 #

    Tem como resolver o mesmo problema acima com menos gambi…
    Tem umas anotacoes nas classes geradas que da problemas… inclusive uma anotacao localizada na package-info.java…

    ABRACOS

  21. Mateus Leal 05/11/2011 at 03:22 #

    Olá, alguem poderia me passar um tutorial (em portugues de preferencia) de como usar a classe XStrean, estou precisando apenas ler dados em arquivos XML e depois armazenar dinovo nos mesmos arquivos lidos (caso mude algum valor). Obrigado

  22. Fi 19/12/2011 at 08:38 #

    Para quem precisar apenas processar um XML e extrair dados diretamente dele, sem transformar em objetos, a melhor biblioteca que eu já vi é a JDOM!
    http://www.jdom.org/

  23. Luana 17/07/2012 at 11:32 #

    Muito bom o post, vou começar a trabalhar com o jaxb agora e estava totalmente perdida, com o post já me deu uma luz de como funciona. Obrigada e parabéns.

  24. GRO 14/12/2012 at 16:57 #

    Olá. Alguém sabe como carregar também o List de motoristas? Carrega o carro mas o seu List de motoristas está null. Tem que fazer isso manualmente? Grato.

  25. Aquila 17/12/2012 at 10:17 #

    Bom dia li o tutorial e segui para desenvolver um consumidor para o ws da sefaz e me serviu bem, mas estou tendo problemas na hora de fazer assinatura digital do xml com o JAXB tem alguma dica de como fazer isso?

  26. Leonardo 18/03/2013 at 08:54 #

    Como que faz para mudar a codificação do xml ja criado com, de encoding=”UTF-8″, para encoding=”ISO-8859-1″.
    Obrigado!

  27. Guilherme de Almeida Moreira 18/03/2013 at 17:59 #

    Olá Leonardo, pelo que eu lembro você pode setar uma propriedade no Marshaller:

    marshaller.setProperty("jaxb.encoding","ISO-8859-1");

    Se não for exatamente este código é parecido com ele, para uma melhor referência dê uma conferida no javadoc dele.

  28. Gustavo Paiva 30/04/2014 at 15:30 #

    Amigo, valeu pelas dicas. Muito bom o artigo.

  29. Camila 06/11/2014 at 16:35 #

    Olá, vocês sabem como eu posso gerar meu xsd com minOccurs=”1″ e maxOccurs=”1″? Tentei anotar o atributo com @XmlElement(name = “quantidadeProduto”, nillable = false, required = true) mas não funcionou.

    Valeu.

  30. Dhift 19/02/2015 at 12:35 #

    Pode explicar a lógica por trás do xsd?

  31. Paulo Silveira 21/02/2015 at 18:45 #

    Dhift, ele é o schema, é quem define o “esqueleto” que todo XML daquele tipo ali deve ter. Então se você vai mandar um xml com os dados de uma nota fiscal, ela precisa seguir uma ‘fórmula’ que a receita federal atende. isso é feito através do xsd, inclusive.

  32. Alexandre 13/03/2015 at 14:41 #

    É possível gerar as classes atreves de um arquivo xsd com jaxb

  33. Rodrigo 08/12/2015 at 17:47 #

    Olá, alguém já usou JAXB para processar uma taxonomia XBRL (árvore de relacionamentos entre xsd e xml), ou seja, apontar um xsd e obter todos os seus relacionamentos em forma de objetos?

  34. Fernando 26/08/2016 at 21:53 #

    Muito bom o post!

  35. Lucas F 06/03/2017 at 14:37 #

    Olá

    Gostaria de saber se tem como eu gerar um conjunto de strings que satisfaçam uma expressão do XSD, alguém pode me dar uma luz ? algum caminho ? ou um gerador sei la.

    Obrigado

  36. Edward Klein 26/05/2017 at 15:24 #

    Espero q vcs nunca apaguem esse post, pq toda vez que preciso gerar as classes de importação eu volto pra lembrar hahahaha

Deixe uma resposta