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.