10 de junho de 2011

XStream: Criando e lendo arquivos XML

Caso ainda não tenha precisado criar ou ler arquivos XML em Java, pelo menos já parou pra pensar como fazer isso? Criar uma String gigante concatenando valores e tags? Hoje em dia não há necessidade de se utilizar métodos tão "complexos" assim. Existe uma biblioteca chamada de XStream, onde objetos podem ser transformados em XML, e vice versa.
Obs: o XStream gera uma String em formato de XML, e não o arquivo XML propriamente dito, mas este é gerado com a ajuda do FileOutputStream do Java.
Então para fazer este exemplo vamos precisar de dois .jar, um é do próprio XStream e o outro é o Dom4J. Para baixar os dois juntos, clique aqui.

Estrutura do projeto
Nosso projeto irá ficar com essa estrutura:


Depois de muito tempo venho editar esta postagem sobre XStream...
Dentro do pacote model temos duas classes, um é o Endereco e a outra é Pessoa.
Classe Endereco:
@XStreamAlias("endereco")
public class Endereco {

    private String rua;
    private String cidade;

    //gettters e setters
}
A anotação @XStreamAlias é necessária quando vai fazer a leitura de uma String XML.
A Classe Pessoa:
public class Pessoa {

    private String nome;
    private String cpf;
    private List<Endereco> enderecos;
  
    //getters e setters
}

Bom agora vamos ver o que desejamos fazer e como fazer... Vamos começar pelo mais simples, caso queira gerar um XML de um endereço que está dessa forma:
<endereco>
  <rua>Rua das araras</rua>
  <cidade>Primavera do Leste</cidade>
</endereco>

Eu preciso do seguinte código:
private static void gravaEndereco() {
        Endereco endereco = new Endereco();
        endereco.setCidade("Primavera do Leste");
        endereco.setRua("Rua das araras");

        XStream xStream = new XStream();
        xStream.alias("endereco", Endereco.class);
       
        File arquivo = new File("endereco.xml");
        FileOutputStream gravar;
        try {
            gravar = new FileOutputStream(arquivo);
            gravar.write(xStream.toXML(endereco).getBytes());
            gravar.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

Lembrando que a parte de gravar o arquivo não é do XStream, a String é gerada pelo seguinte código: xStream.toXML(endereco), o mesmo vale para os próximos métodos...

Agora caso queira gravar uma lista de endereços em XML, que fique dessa forma:
<enderecos>
  <endereco>
    <rua>Rua abc</rua>
    <cidade>Cuiaba</cidade>
  </endereco>
  <endereco>
    <rua>Avenida São João</rua>
    <cidade>Primavera do Leste</cidade>
  </endereco>
</enderecos>

É necessário o seguinte método:
private static void gravaListaEndereco() {
        List<Endereco> enderecos = new ArrayList<Endereco>();

        Endereco e1 = new Endereco();
        e1.setCidade("Cuiaba");
        e1.setRua("Rua abc");
        enderecos.add(e1);

        Endereco e2 = new Endereco();
        e2.setCidade("Primavera do Leste");
        e2.setRua("Avenida São João");
        enderecos.add(e2);

        XStream xStream = new XStream();
        xStream.alias("enderecos", List.class);
        xStream.alias("endereco", Endereco.class);

        File arquivo = new File("enderecos.xml");
        FileOutputStream gravar;
        try {
            gravar = new FileOutputStream(arquivo);
            gravar.write(xStream.toXML(enderecos).getBytes());
            gravar.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

Agora vamos ver algo mais complicado, que é gravar objetos que contenham listas dentro dele... ou seja vamos criar um XML que represente uma lista de pessoas (nosso model lá do início):
<pessoas>
  <pessoa>
    <nome>Andii</nome>
    <cpf>123.123.123-34</cpf>
    <enderecos>
      <endereco>
        <rua>Rua 123</rua>
        <cidade>Primavera do Leste</cidade>
      </endereco>
    </enderecos>
  </pessoa>
  <pessoa>
    <nome>Maria</nome>
    <cpf>345.656.235-98</cpf>
    <enderecos>
      <endereco>
        <rua>Avenida das flores</rua>
        <cidade>Cuiabá</cidade>
      </endereco>
      <endereco>
        <rua>Rua das abelhas</rua>
        <cidade>Cuiabá</cidade>
      </endereco>
    </enderecos>
  </pessoa>
</pessoas>
Para gerar esta estrutura precisamos o seguinte método:
private static void gravaListaCompleta() {
        Pessoa pessoa1 = new Pessoa();
        pessoa1.setNome("Andii");
        pessoa1.setCpf("123.123.123-34");

        Endereco endereco1 = new Endereco();
        endereco1.setCidade("Primavera do Leste");
        endereco1.setRua("Rua 123");

        List<Endereco> enderecos1 = new ArrayList<Endereco>();
        enderecos1.add(endereco1);
        pessoa1.setEnderecos(enderecos1);

        Pessoa pessoa2 = new Pessoa();
        pessoa2.setNome("Maria");
        pessoa2.setCpf("345.656.235-98");

        Endereco endereco2 = new Endereco();
        endereco2.setCidade("Cuiabá");
        endereco2.setRua("Avenida das flores");

        Endereco endereco3 = new Endereco();
        endereco3.setCidade("Cuiabá");
        endereco3.setRua("Rua das abelhas");

        List<Endereco> enderecos2 = new ArrayList<Endereco>();
        enderecos2.add(endereco2);
        enderecos2.add(endereco3);
        pessoa2.setEnderecos(enderecos2);

        List<Pessoa> pessoas = new ArrayList<Pessoa>();
        pessoas.add(pessoa1);
        pessoas.add(pessoa2);

        XStream xStream = new XStream();

        xStream.alias("pessoas", List.class);
        xStream.alias("pessoa", Pessoa.class);
        xStream.alias("endereco", Endereco.class);

        File arquivo = new File("pessoas.xml");
        FileOutputStream gravar;
        try {
            gravar = new FileOutputStream(arquivo);
            gravar.write(xStream.toXML(pessoas).getBytes());
            gravar.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
E enfim para lermos uma String XML, precisamos do método abaixo, para o exemplo de leitura eu estarei utilizando o arquivo gerado em um dos métodos acima, aquele que gera uma lista de endereços:
private static void lerXML() {
        try {
            XStream xStream = new XStream(new Dom4JDriver());
            xStream.alias("enderecos", ArrayList.class);
            xStream.processAnnotations(Endereco.class);

            BufferedReader input = new BufferedReader(new FileReader("enderecos.xml"));
            ArrayList<Endereco> enderecos = (ArrayList) xStream.fromXML(input);
            input.close();

            for (Endereco endereco : enderecos) {
                System.out.println("Endereço: " + endereco.getRua() + " - " + endereco.getCidade());
            }

        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
Veja que eu leio o arquivo utilizando BufferedReader e tendo esse arquivo "carregado" utilizo do XStream para gerar uma lista para mim... Lembrando que dessa vez eu preciso da anotação lá na classe Endereco, o resultado da leitura do método acima é:
Endereço: Rua abc - Cuiaba
Endereço: Avenida São João - Primavera do Leste
Bom... peço desculpas pela demora a fazer as explicações do XStream... Mas está aí :)

Observação final: como vocês podem ver, eu não coloquei um endereço fixo de onde armazenar meus arquivos, era apenas new file("endereco.xml") ... para quem não sabe isso gera o arquivo dentro da pasta do projeto.

24 comentários:

  1. Gostaria de parabeniza-la pelo blog.
    Não sou seu aluno, mas posso dizer que já aprendi mais com seus tutoriais que em muitas aulas que tive.

    ResponderExcluir
  2. Olá Gil! Obrigada, fico feliz por saber disso! :-)

    ResponderExcluir
  3. Boa noite.
    Sua explicações são ótimas eu aprendo muito.
    Queria muito que se você tivesse como fizesse um passo a passo com o spring 3

    ResponderExcluir
  4. Olá Igor! Obrigada... Assim que eu aprender a mexer com o Spring 3 eu posso fazer umas postagens sim... por enquanto só sei mexer com o Spring Security 3.0.5 (tem até uma postagem no blog) :)

    ResponderExcluir
  5. Olá Andi.
    Poderia me tirar uma duvida sobre como criar um header no padrão primefaces.
    Gostaria de montar um header semelhante ao criado no showcase do primefaces, que quando eu trocasse a skin ele alterasse o fundo tambem.
    Espero não estar encomodando.

    ResponderExcluir
  6. Karalho!!! Uma mulher no javaEE, um caso muito raro, não se ve muito disso por ae...

    parabéns pelo blog.. Abraços

    Thiago Luiz | Mirante - RO / Recife - PE

    ResponderExcluir
  7. Oi Andii,

    Muito bom o seu tutorial com o XStream, eu estava mesmo procurando algum pacote para que poder usar com webservices para Java ME sem precisar usar o Jax ou afins... está muito bom o tutorial. Muito obrigado pela iniciativa, te prometo que assim que eu puder e tiver a iluminação necessária, posso te ajudar em alguma seria uma forma de retribuir.
    Abraços

    ResponderExcluir
  8. Olá gabrielsimas! Novas idéias serão bem vindas! ;)

    ResponderExcluir
  9. Olá Thiago Luiz! Obrigada! Realmente nessa área mulheres são raras rs

    ResponderExcluir
  10. Muito bom a explicação cara!
    se vc poder me ajudar gostaria de saber como que eu faço para lista "cadastros" (id, nome, telefone) do java DB e gerar um arquivo XML.
    se puder me ajudar agradeço!
    ricardo.zkb@hotmail.com

    ResponderExcluir
  11. Olá Zakabi, o método gravaListaEndereco() faz isso, é só vc adaptar ele!, já traga a lista carregada do BD e pronto!

    ResponderExcluir
  12. Ok muito obrigado vou ver se consigo!

    ResponderExcluir
  13. Ola, vc poderia me dizer oq q ta errado no meu codigo?

    quero fazer a leitura de um arquivo chamado "aluno.xml" e armazenar os dados em um objeto chamado aluno do tipo Aluno (que é o nome da minha classe). Esse arquivo é selecionado por um jFileChoser

    o codigo ta um poco atrapalhado pois primeiro quero fazer so um teste e depois fazer a versão final do programa, ok? segue abaixo

    try {
    JFileChooser fileChooser = new JFileChooser(".");
    fileChooser.setFileFilter(new FileNameExtensionFilter("Apenas XML","xml"));
    int retorno = fileChooser.showOpenDialog(null);

    if (retorno == JFileChooser.APPROVE_OPTION) {

    Aluno aluno = new Aluno();
    BufferedReader input = new BufferedReader(new FileReader(fileChooser.getSelectedFile()));
    XStream XStream = new XStream(new Dom4JDriver());
    aluno = (Aluno) XStream.fromXML(input);

    //isso vai sumir depois dos testes
    NomeUsuario.setText(aluno.getNome());
    }
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    }

    ResponderExcluir
  14. Andii

    se eu quiser ver, os dados das pessoas (pessoas.xml), seria assim:

    private static void lerXML() {
    try {
    XStream xStream = new XStream(new Dom4JDriver());

    xStream.alias("pessoas", ArrayList.class);
    xStream.processAnnotations(Pessoa.class);

    xStream.alias("enderecos", ArrayList.class);
    xStream.processAnnotations(Endereco.class);

    BufferedReader input = new BufferedReader(new FileReader("pessoas.xml"));
    ArrayList pessoas = (ArrayList) xStream.fromXML(input);

    for(Pessoa p : pessoas){
    System.out.println(p.getNome());
    System.out.println(p.getCpf());
    for(Endereco endereco : p.getEnderecos()){
    System.out.println(endereco.getCidade());
    System.out.println(endereco.getRua());
    }
    System.out.println();
    }
    } catch (IOException ex) {
    ex.printStackTrace();
    }
    }

    ResponderExcluir
  15. Este comentário foi removido pelo autor.

    ResponderExcluir
  16. Olá, primeiramente parabéns pelo tutorial!
    Agora vamos as perguntas... rsrs

    Para que eu possa pegar os dados de uma tag no xml eu preciso necessariamente ter
    um objeto relacionado a este xml?
    Ex: no xml tenho a tag CNPJ e muuuuitas outras, eu queria pegar apenas o conteúdo da tag CNPJ tem como?

    ResponderExcluir
  17. Ola Paulo, eu acredito que vc consiga sim pegar apenas o CNPJ, é só montar a estrutura correta até chegar no CNPJ. Não é obrigatório pegar todos os dados do xml, ele só vai pegar os que tiver anotado.

    ResponderExcluir
  18. Olá Andii, tudo bem?
    Antes de tudo, sua explicação se não é foi uma das melhores que encontrei na internet, ainda mais eu que estou iniciando na geração de xml.
    Minha dúvida é a seguinte, como faço para pegar algo que está no banco de dados e assim gerar essa informação no xml.
    Esse é parte do seu código:
    [code]
    Pessoa pessoa1 = new Pessoa();
    pessoa1.setNome("Andii");
    pessoa1.setCpf("123.123.123-34");
    [/code]
    Queria que no lugar de ("Andii"), fosse o comando para pegar esse nome do banco, sei que essa pegunta pode parecer fácil ou que eu não procurei no google, mas procurei e não achei nada que entendece. Por favor poderia me ajudar?
    Desde já agradeço, Obrigado.

    ResponderExcluir
  19. Oi andii, na hora da leitura, está aparecendo o seguinte erro:
    "Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: Participante cannot be cast to java.util.ArrayList"

    saberia me dizer por que?

    dei uma pesquisada, mas não achei nada :/

    ResponderExcluir
    Respostas
    1. Bom dia Marc, este erro quer dizer que ele não está conseguindo converter o objeto Participante em um ArrayList, reveja seu código.

      Excluir
  20. Nelson Bayma,
    Boa tarde Andii,
    Primeiro Parabéns, me ajudou muito a primeira parte, mas estou tendo um problema na geração do XML com lista dentro deles. Na linha 29 do método "pessoa2.setEnderecos(enderecos2);" não consigo fazer isto funcionar, como sou neófito no assunto não consigo criar a classe modelo para que funcione. Se puder me de a dica de como montar estes enderecos na classe modelo pessoa.
    Grato, Nelson Bayma . nbayma@hotmail.com

    ResponderExcluir

Deixe seu comentário... ;)