XStream User Guide: Convertendo objetos para XML

XStream User Guide: Convertendo objetos para XML

1. Visão geral

Neste tutorial, aprenderemos como usar a bibliotecaXStream para serializar objetos Java para XML.

2. Recursos

Existem alguns benefícios interessantes no uso do XStream para serializar e desserializar XML:

  • Configurado corretamente, produz muitoclean XML

  • Oferece oportunidades significativas paracustomization da saída XML

  • Suporte paraobject graphs, incluindo referências circulares

  • Para a maioria dos casos de uso, a instância XStream éthread-safe, once configured (há advertências ao usar anotações)

  • Mensagens claras são fornecidas duranteexception handling para ajudar a diagnosticar problemas

  • A partir da versão 1.4.7, temossecurity features disponíveis para proibir a serialização de certos tipos

3. Configuração do Projeto

Para usar o XStream em nosso projeto, adicionaremos a seguinte dependência do Maven:


    com.thoughtworks.xstream
    xstream
    1.4.9

4. Uso básico

A classeXStream é uma fachada para a API. Ao criar uma instância deXStream, precisamos cuidar também dos problemas de segurança do thread:

XStream xstream = new XStream();

Depois que uma instância é criada e configurada, ela pode ser compartilhada entre vários encadeamentos para organizar / cancelar a organização, a menos que você ative o processamento da anotação.

4.1. Drivers

Vários drivers são suportados, comoDomDriver,StaxDriver,XppDriver e mais. Esses drivers têm características diferentes de desempenho e uso de recursos.

O driver XPP3 é usado por padrão, mas é claro que podemos mudar facilmente o driver:

XStream xstream = new XStream(new StaxDriver());

4.2. Gerando XML

Vamos começar definindo um POJO simples para -Customer:

public class Customer {

    private String firstName;
    private String lastName;
    private Date dob;

    // standard constructor, setters, and getters
}

Vamos agora gerar uma representação XML do objeto:

Customer customer = new Customer("John", "Doe", new Date());
String dataXml = xstream.toXML(customer);

Usando as configurações padrão, a seguinte saída é produzida:


    John
    Doe
    1986-02-14 03:46:16.381 UTC

A partir dessa saída, podemos ver claramente que a tag que contém usa o nome de classe totalmente qualificado deCustomer por padrão.

Existem muitos motivos pelos quais podemos decidir que o comportamento padrão não atende às nossas necessidades. Por exemplo, podemos não estar confortáveis ​​em expor a estrutura de pacotes do nosso aplicativo. Além disso, o XML gerado é significativamente maior.

5. Apelido

Umalias é um nome que desejamos usar para elementos em vez de usar nomes padrão.

Por exemplo, podemos substituircom.example.pojo.Customer porcustomer registrando um alias para a classeCustomer. Também podemos adicionar aliases para propriedades de uma classe. Usando aliases, podemos tornar nossa saída XML muito mais legível e menos específica para Java.

5.1. Aliases de classe

Os aliases podem ser registrados programaticamente ou usando anotações.

Vamos agora anotar nossa classeCustomer com@XStreamAlias:

@XStreamAlias("customer")

Agora precisamos configurar nossa instância para usar esta anotação:

xstream.processAnnotations(Customer.class);

Como alternativa, se desejarmos configurar um alias programaticamente, podemos usar o código abaixo:

xstream.alias("customer", Customer.class);

Seja usando o alias ou a configuração programática, a saída para um objetoCustomer será muito mais limpa:


    John
    Doe
    1986-02-14 03:46:16.381 UTC

5.2. Aliases de campo

Também podemos adicionar aliases para campos usando a mesma anotação usada para classes de alias. Por exemplo, se quisermos que o campofirstName seja substituído porfn na representação XML, podemos usar a seguinte anotação:

@XStreamAlias("fn")
private String firstName;

Como alternativa, podemos alcançar o mesmo objetivo programaticamente:

xstream.aliasField("fn", Customer.class, "firstName");

O métodoaliasField aceita três argumentos: o apelido que desejamos usar, a classe na qual a propriedade é definida e o nome da propriedade que desejamos apelidar.

Qualquer que seja o método usado, a saída é a mesma:


    John
    Doe
    1986-02-14 03:46:16.381 UTC

5.3. Aliases padrão

Existem vários aliases pré-registrados para as classes - aqui estão alguns deles:

alias("float", Float.class);
alias("date", Date.class);
alias("gregorian-calendar", Calendar.class);
alias("url", URL.class);
alias("list", List.class);
alias("locale", Locale.class);
alias("currency", Currency.class);

6. Colecções

Agora vamos adicionar uma lista deContactDetails dentro da classeCustomer.

private List contactDetailsList;

Com configurações padrão para manipulação de coleção, esta é a saída:


    John
    Doe
    1986-02-14 04:14:05.874 UTC
    
        
            6673543265
            0124-2460311
        
        
            4676543565
            0120-223312
        
    

Vamos supor que precisamos omitir as tags paicontactDetailsList, e queremos apenas que cada elementoContactDetails seja filho do elementocustomer. Vamos modificar nosso exemplo novamente:

xstream.addImplicitCollection(Customer.class, "contactDetailsList");

Agora, quando o XML é gerado, as tags raiz são omitidas, resultando no XML abaixo:


    John
    Doe
    1986-02-14 04:14:20.541 UTC
    
        6673543265
        0124-2460311
    
    
        4676543565
        0120-223312
    

O mesmo também pode ser alcançado usando anotações:

@XStreamImplicit
private List contactDetailsList;

7. Conversores

O XStream usa um mapa de instânciasConverter, cada uma com sua própria estratégia de conversão. Eles convertem os dados fornecidos para um formato específico em XML e vice-versa.

Além de usar os conversores padrão, podemos modificar os padrões ou registrar conversores personalizados.

7.1. Modificando um conversor existente

Suponha que não estamos satisfeitos com a maneira como as tagsdob foram geradas_ using the default settings. We can modify the custom converter for _Date fornecidas pelo XStream (DateConverter):

xstream.registerConverter(new DateConverter("dd-MM-yyyy", null));

O acima irá produzir a saída no formato “dd-MM-yyyy”:


    John
    Doe
    14-02-1986

7.2. Conversores personalizados

Também podemos criar um conversor personalizado para obter a mesma saída da seção anterior:

public class MyDateConverter implements Converter {

    private SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");

    @Override
    public boolean canConvert(Class clazz) {
        return Date.class.isAssignableFrom(clazz);
    }

    @Override
    public void marshal(
      Object value, HierarchicalStreamWriter writer, MarshallingContext arg2) {
        Date date = (Date)value;
        writer.setValue(formatter.format(date));
    }

    // other methods
}

Finalmente, registramos nossa classeMyDateConverter conforme abaixo:

xstream.registerConverter(new MyDateConverter());

Também podemos criar conversores que implementam a interfaceSingleValueConverter, que é projetada para converter um objeto em uma string.

public class MySingleValueConverter implements SingleValueConverter {

    @Override
    public boolean canConvert(Class clazz) {
        return Customer.class.isAssignableFrom(clazz);
    }

    @Override
    public String toString(Object obj) {
        SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
        Date date = ((Customer) obj).getDob();
        return ((Customer) obj).getFirstName() + ","
          + ((Customer) obj).getLastName() + ","
          + formatter.format(date);
    }

    // other methods
}

Finalmente, registramosMySingleValueConverter:

xstream.registerConverter(new MySingleValueConverter());

UsandoMySingleValueConverter, a saída XML para umCustomer é a seguinte:

John,Doe,14-02-1986

7.3. Prioridade do conversor

Ao registrar objetosConverter, também é possível definir seu nível de prioridade.

Os conversores podem ser registrados com uma prioridade explícita. Por padrão, eles são registrados no XStream.PRIORITY_NORMAL. Conversores de mesma prioridade serão usados ​​na sequência reversa em que foram registrados. O conversor padrão, ou seja, o conversor que será usado se nenhum outro conversor registrado for adequado, pode ser registrado com a prioridade XStream.PRIORITY_VERY_LOW. O XStream usa por padrão o ReflectionConverter como o conversor de fallback.

A API fornece vários valores de prioridade nomeados: **

private static final int PRIORITY_NORMAL = 0;
private static final int PRIORITY_LOW = -10;
private static final int PRIORITY_VERY_LOW = -20;

8. Omitindo campos

Podemos omitir campos de nosso XML gerado usando anotações ou configuração programática. Para omitir um campo usando uma anotação, simplesmente aplicamos a anotação@XStreamOmitField ao campo em questão:

@XStreamOmitField
private String firstName;

Para omitir o campo programaticamente, usamos o seguinte método:

xstream.omitField(Customer.class, "firstName");

Seja qual for o método selecionado, a saída é a mesma:


    Doe
    14-02-1986

9. Campos de Atributo

Às vezes, podemos desejar serializar um campo como um atributo de um elemento, e não como o próprio elemento. Suponha que adicionemos um campocontactType:

private String contactType;

Se quisermos definircontactType como um atributo XML, podemos usar a anotação@XStreamAsAttribute:

@XStreamAsAttribute
private String contactType;

Como alternativa, podemos alcançar o mesmo objetivo programaticamente:

xstream.useAttributeFor(ContactDetails.class, "contactType");

A saída de qualquer um dos métodos acima é a mesma:


    6673543265
    0124-2460311

10. Concorrência

O modelo de processamento do XStream apresenta alguns desafios. Depois que a instância é configurada, ela é segura para threads.

É importante observar que o processamento de anotações modifica a configuração imediatamente antes do empacotamento / descompactação. E, portanto, se exigirmos que a instância seja configurada rapidamente usando anotações, geralmente é uma boa ideia usar uma instânciaXStream separada para cada thread.

11. Conclusão

Neste artigo, abordamos o básico do uso do XStream para converter objetos em XML. Também aprendemos sobre personalizações que podemos usar para garantir que a saída XML atenda às nossas necessidades. Por fim, analisamos os problemas de segurança de threads com anotações.

No próximo artigo desta série, aprenderemos sobre a conversão de XML novamente em objetos Java.

O código-fonte completo deste artigo pode ser baixado do linkGitHub repository.