Cancelando a remoção de datas usando JAXB
1. Introdução
Neste tutorial,we’re going to see how to unmarshal date objects with different formats using JAXB.
Primeiro, vamos cobrir o formato de data do esquema padrão. Em seguida, exploraremos como usar formatos diferentes. Também veremos como podemos lidar com um desafio comum que surge com essas técnicas.
2. Esquema para ligação a Java
Primeiro,we need to understand the relationship between the XML Schema and Java data types. Em particular, estamos interessados no mapeamento entre um esquema XML e objetos de data Java.
De acordo com oSchema to Java mapping, há três tipos de dados do esquema que precisamos levar em consideração:xsd:date, xsd:time and xsd:dateTime. Como podemos ver, todos eles são mapeados parajavax.xml.datatype.XMLGregorianCalendar.
We also need to understand the default formats for these XML Schema types. Os tipos de dadosxsd:dateexsd:time têm os formatos “YYYY-MM-DD”e“hh:mm:ss”. The xsd:dateTime format is “YYYY-MM-DDThh:mm:ss” onde “T” é um separador que indica o início da seção de tempo.
3. Usando o formato de data do esquema padrão
Vamos construir um exemplo que desempacota objetos de data. Vamos nos concentrar no tipo de dadosxsd:dateTime porque é um superconjunto dos outros tipos.
Vamos usar um arquivo XML simples que descreve um livro:
Book1
1979-10-21T03:31:12
Queremos mapear o arquivo para o objeto JavaBook correspondente:
@XmlRootElement(name = "book")
public class Book {
@XmlElement(name = "title", required = true)
private String title;
@XmlElement(name = "published", required = true)
private XMLGregorianCalendar published;
@Override
public String toString() {
return "[title: " + title + "; published: " + published.toString() + "]";
}
}
Por fim, precisamos criar um aplicativo cliente que converta os dados XML em objetos Java derivados do JAXB:
public static Book unmarshalDates(InputStream inputFile)
throws JAXBException {
JAXBContext jaxbContext = JAXBContext.newInstance(Book.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
return (Book) jaxbUnmarshaller.unmarshal(inputFile);
}
No código acima, definimos umJAXBContext que é o ponto de entrada para a API JAXB. Então, nós usamos um JAXBUnmarshaller em um fluxo de entrada para ler nosso objeto:
Se executarmos o código acima e imprimirmos o resultado, obteremos o seguinte objetoBook:
[title: Book1; published: 1979-11-28T02:31:32]
Devemos observar que, embora o mapeamento padrão paraxsd:dateTime sejaXMLGregorianCalendar, também poderíamos ter usado os tipos Java mais comuns:java.util.Dateejava.util.Calendar, de acordo comJAXB user guide.
4. Usando um formato de data personalizado
O exemplo acima funciona porque estamos usando o formato de data do esquema padrão,“YYYY-MM-DDThh:mm:ss”.
Mas e se quisermos usar outro formato como“YYYY-MM-DD hh:mm:ss”, eliminando o delimitador“T”? Se substituíssemos o delimitador por um caractere de espaço em nosso arquivo XML, o desserialização padrão falharia.
4.1. Construindo umXmlAdapter personalizado
Para usar um formato de data diferente, precisamos definir umXmlAdapter.
Vejamos também como mapear o tipoxsd:dateTime para um objetojava.util.Date com nossoXmlAdapter: personalizado
public class DateAdapter extends XmlAdapter {
private static final String CUSTOM_FORMAT_STRING = "yyyy-MM-dd HH:mm:ss";
@Override
public String marshal(Date v) {
return new SimpleDateFormat(CUSTOM_FORMAT_STRING).format(v);
}
@Override
public Date unmarshal(String v) throws ParseException {
return new SimpleDateFormat(CUSTOM_FORMAT_STRING).parse(v);
}
}
Neste adaptador,we’ve usedSimpleDateFormat to format our date. Precisamos ter cuidado, poisthe SimpleDateFormat is not thread-safe. Para evitar que vários threads tenham problemas com um objetoSimpleDateFormat compartilhado, estamos criando um novo cada vez que preciso disso.
4.2. Os internos deXmlAdapter
Como podemos ver, oXmlAdapter has two type parameters, neste caso,StringeDate. O primeiro é o tipo usado dentro do XML e é chamado de tipo de valor. Nesse caso, JAXB sabe como converter um valor XML emString. O segundo é chamado de tipo de ligação e se relaciona com o valor em nosso objeto Java.
O objetivo de um adaptador é converter entre o tipo de valor e um tipo de limite, de uma forma que o JAXB não pode fazer por padrão.
Para construir umXmlAdapter personalizado, temos que substituir dois métodos:XmlAdapter.marshal()eXmlAdapter.unmarshal().
Durante o desempacotamento, a estrutura de ligação JAXB primeiro desempacota a representação XML paraStringe, em seguida, chamaDateAdapter.unmarshal() para adaptar o tipo de valor aDate. Durante o empacotamento, a estrutura de ligação JAXB invocaDateAdapter.marshal() para adaptarDate aString, que é então empacotado para uma representação XML.
4.3. Integrando através das anotações JAXB
ODateAdapter funciona como um plugin para JAXB e vamos anexá-lo ao nosso campo de data usando a anotação@XmlJavaTypeAdapter. The @XmlJavaTypeAdapter annotation specifies the use of an XmlAdapter for custom unmarshalling:
@XmlRootElement(name = "book")
public class BookDateAdapter {
// same as before
@XmlElement(name = "published", required = true)
@XmlJavaTypeAdapter(DateAdapter.class)
private Date published;
// same as before
}
Também estamos usando as anotaçõesstandard JAXB annotations:@XmlRootElemente@XmlElement.
Finalmente, vamos executar o novo código:
[title: Book1; published: Wed Nov 28 02:31:32 EET 1979]
5. Desmarcar datas em Java 8
Java 8 introduziu um novoDate/Time API. Aqui, vamos nos concentrar na classeLocalDateTime, que é uma das mais comumente usadas.
5.1. Construindo umLocalDateTime baseado emXmlAdapter
Por padrão,JAXB cannot automatically bind an xsd:dateTime value to a LocalDateTime objeto independentemente do formato de data. Para converter um valor de data do Esquema XML de ou para um objetoLocalDateTime, precisamos definir outroXmlAdapter semelhante ao anterior:
public class LocalDateTimeAdapter extends XmlAdapter {
private DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
public String marshal(LocalDateTime dateTime) {
return dateTime.format(dateFormat);
}
@Override
public LocalDateTime unmarshal(String dateTime) {
return LocalDateTime.parse(dateTime, dateFormat);
}
}
Nesse caso,we’ve used a DateTimeFormatter instead of a SimpleDateFormat. O primeiro foi introduzido no Java 8 e é compatível com a nova APIDate/Time.
Observe que as operações de conversão podem compartilhar um objetoDateTimeFormatter porquethe DateTimeFormatter is thread-safe.
5.2. Integrando o novo adaptador
Agora, vamos substituir o adaptador antigo pelo novo em nossa classeBook e tambémDate comLocalDateTime:
@XmlRootElement(name = "book")
public class BookLocalDateTimeAdapter {
// same as before
@XmlElement(name = "published", required = true)
@XmlJavaTypeAdapter(LocalDateTimeAdapter.class)
private LocalDateTime published;
// same as before
}
Se executarmos o código acima, obteremos o resultado:
[title: Book1; published: 1979-11-28T02:31:32]
Observe queLocalDateTime.toString() adiciona o delimitador“T” entre a data e a hora.
6. Conclusão
Neste tutorial, exploramosunmarshalling dates using JAXB.
Primeiro, examinamos o mapeamento do tipo de dados Esquema XML para Java e criamos um exemplo usando o formato de data padrão do Esquema XML.
Em seguida, aprendemos como usar um formato de data personalizado com base em umXmlAdapter personalizado e vimos como lidar com a segurança de thread deSimpleDateFormat.
Por fim, aproveitamos a API Java 8 Date / Time superior e segura para threads e datas não ordenadas com formatos personalizados.
Como sempre, o código-fonte usado no tutorial está disponívelover on GitHub.