Comment spécifiez-vous le format de date utilisé lorsque JAXB marshale xsd: dateTime?

86

Lorsque JAXB rassemble un objet date ( XMLGregorianCalendar) en un élément xsd: dateTime. Comment pouvez-vous spécifier le format du XML résultant?

Par exemple: le format de données par défaut utilise des millisecondes, <StartDate>2012-08-21T13:21:58.000Z</StartDate> j'ai besoin d'omettre les millisecondes. <StartDate>2012-08-21T13:21:58Z</StartDate>

Comment puis-je spécifier le formulaire de sortie / le format de date que je souhaite utiliser? J'utilise javax.xml.datatype.DatatypeFactorypour créer l' XMLGregorianCalendarobjet.

XMLGregorianCalendar xmlCal = datatypeFactory.newXMLGregorianCalendar(cal);
Jeune Fu
la source

Réponses:

126

Vous pouvez utiliser un XmlAdapterpour personnaliser la façon dont un type de date est écrit en XML.

package com.example;

import java.text.SimpleDateFormat;
import java.util.Date;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class DateAdapter extends XmlAdapter<String, Date> {

    private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Override
    public String marshal(Date v) throws Exception {
        synchronized (dateFormat) {
            return dateFormat.format(v);
        }
    }

    @Override
    public Date unmarshal(String v) throws Exception {
        synchronized (dateFormat) {
            return dateFormat.parse(v);
        }
    }

}

Ensuite, vous utilisez l' @XmlJavaTypeAdapterannotation pour spécifier que le XmlAdapterdoit être utilisé pour un champ / une propriété spécifique.

@XmlElement(name = "timestamp", required = true) 
@XmlJavaTypeAdapter(DateAdapter.class)
protected Date timestamp; 

À l'aide d'un fichier de liaison xjb:

<xjc:javaType name="java.util.Date" xmlType="xs:dateTime"
        adapter="com.example.DateAdapter"/>

produira l'annotation mentionnée ci-dessus.
(En ajoutant éventuellement l' xjcespace de noms: xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc")

bdoughan
la source
2
Merci pour cette réponse! Est-il possible d'ajouter l'annotation via le xsd ou un fichier de liaison? Je n'ai trouvé que votre article de blog fréquemment cité sur bindings.xml, mais cela couvre d'autres aspects, je pense.
guerda
9
Comme @PeterRader l'a mentionné, SimpleDateFormat n'est pas thread-safe - si deux threads devaient entrer simultanément marshal ou unmarshal, vous pourriez obtenir des résultats très imprévisibles. Cela serait très difficile à reproduire dans des tests normaux, mais sous charge pourrait se produire et serait extrêmement difficile à diagnostiquer. Il est préférable de créer un nouveau SimpleDateFormat avec marshal et unmarshal (mais utilisez une chaîne de format statique si nécessaire).
Colselaw
1
J'ai fait ça et ça a presque marché. Cependant, j'obtenais une Class has two properties of the same name "timeSeries"erreur - cela a été résolu en mettant l'annotation au getter et non au niveau du membre. (Merci à @megathor de stackoverflow.com/questions/6768544/… )
gordon613
1
@ gordon613 - Cet article fournira des informations supplémentaires sur l'emplacement de l'annotation: blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html
bdoughan
3
Puisque le bloc critique est protégé par «synchronisé», il n'y a aucun problème. Il y aura un problème (de performance) si plusieurs appels sont effectués.
Mike Argyriou
17

J'utilise un SimpleDateFormat pour créer le XMLGregorianCalendar, comme dans cet exemple:

public static XMLGregorianCalendar getXmlDate(Date date) throws DatatypeConfigurationException {
    return DatatypeFactory.newInstance().newXMLGregorianCalendar(new SimpleDateFormat("yyyy-MM-dd").format(date));
}

public static XMLGregorianCalendar getXmlDateTime(Date date) throws DatatypeConfigurationException {
    return DatatypeFactory.newInstance().newXMLGregorianCalendar(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(date));
}

La première méthode crée une instance de XMLGregorianCalendar qui est formatée par le marshaller XML comme un xsd: date valide, la seconde méthode donne un xsd: dateTime valide.

Andrea Luciano
la source
2

Moyen très facile pour moi. Formater XMLGregorianCalendar pour le marshalling en java.

Je crée simplement mes données dans le bon format. Le toStringsera appelé produire le bon résultat.

public static final XMLGregorianCalendar getDate(Date d) {
    try {
        return DatatypeFactory.newInstance().newXMLGregorianCalendar(new SimpleDateFormat("yyyy-MM-dd").format(d));
    } catch (DatatypeConfigurationException e) {
        return null;
    }
}
Iván
la source
1

https://www.baeldung.com/jaxb

public class DateAdapter extends XmlAdapter<String, Date> {

    private static final ThreadLocal<DateFormat> dateFormat 
      = new ThreadLocal<DateFormat>() {

        @Override
        protected DateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
    }

    @Override
    public Date unmarshal(String v) throws Exception {
        return dateFormat.get().parse(v);
    }

    @Override
    public String marshal(Date v) throws Exception {
        return dateFormat.get().format(v);
    }
}
Mike
la source
0

Usage:

import com.company.LocalDateAdapter.yyyyMMdd;
...

@XmlElement(name = "PROC-DATE")
@XmlJavaTypeAdapter(yyyyMMdd.class)
private LocalDate processingDate;

LocalDateAdapter

import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

public class LocalDateAdapter extends XmlAdapter<String, LocalDate> {

    public static final class yyyyMMdd extends LocalDateAdapter {
        public yyyyMMdd() {
            super("yyyyMMdd");
        }
    }

    public static final class yyyy_MM_dd extends LocalDateAdapter {
        public yyyy_MM_dd() {
            super("yyyy-MM-dd");
        }
    }

    private final DateTimeFormatter formatter;

    public LocalDateAdapter(String pattern) {
        formatter = DateTimeFormat.forPattern(pattern);
    }

    @Override
    public String marshal(LocalDate date) throws Exception {
        return formatter.print(date);
    }

    @Override
    public LocalDate unmarshal(String date) throws Exception {
        return formatter.parseLocalDate(date);
    }
}
Mike
la source