Wie legen Sie das Datumsformat fest, das verwendet wird, wenn JAXB xsd: dateTime marshallt?

86

Wenn JAXB XMLGregorianCalendarein Datumsobjekt ( ) in ein xsd: dateTime-Element marshallt. Wie können Sie das Format des resultierenden XML angeben?

Beispiel: Das Standarddatenformat verwendet Millisekunden. <StartDate>2012-08-21T13:21:58.000Z</StartDate> Ich muss die Millisekunden weglassen. <StartDate>2012-08-21T13:21:58Z</StartDate>

Wie kann ich das Ausgabeformular / Datumsformat angeben, das verwendet werden soll? Ich benutze javax.xml.datatype.DatatypeFactory, um das XMLGregorianCalendarObjekt zu erstellen .

XMLGregorianCalendar xmlCal = datatypeFactory.newXMLGregorianCalendar(cal);
Junger Fu
quelle

Antworten:

126

Mit a können Sie XmlAdapteranpassen, wie ein Datumstyp in XML geschrieben wird.

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);
        }
    }

}

Anschließend verwenden Sie die @XmlJavaTypeAdapterAnmerkung, um anzugeben, dass die XmlAdapterfür ein bestimmtes Feld / eine bestimmte Eigenschaft verwendet werden soll.

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

Verwenden einer xjb-Bindungsdatei:

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

wird die oben erwähnte Anmerkung erzeugen.
(Durch das Hinzufügen schließlich den xjcNamensraum: xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc")

bdoughan
quelle
2
Danke für diese Antwort! Ist es möglich, die Annotation über die xsd oder eine Bindungsdatei hinzuzufügen? Ich habe nur Ihren häufig zitierten Blogeintrag über bindings.xml gefunden, aber ich denke, dies deckt andere Aspekte ab.
Guerda
9
Wie @PeterRader erwähnt hat, ist SimpleDateFormat nicht threadsicher. Wenn zwei Threads gleichzeitig entweder Marschall oder Unmarschall eingeben, können sehr unvorhersehbare Ergebnisse erzielt werden. Dies wäre bei normalen Tests sehr schwer zu reproduzieren, könnte jedoch unter Last auftreten und wäre äußerst schwierig zu diagnostizieren. Es ist besser, ein neues SimpleDateFormat mit Marschall und Unmarschall zu erstellen (verwenden Sie jedoch bei Bedarf eine statische Formatzeichenfolge).
Colselaw
1
Ich habe das gemacht und es hat fast funktioniert. Ich habe jedoch einen Class has two properties of the same name "timeSeries"Fehler erhalten - dies wurde behoben, indem die Anmerkung am Getter und nicht auf der Mitgliedsebene platziert wurde. (Dank an @megathor von stackoverflow.com/questions/6768544/… )
gordon613
1
@ gordon613 - Dieser Artikel enthält einige zusätzliche Informationen zum Platzieren
bdoughan
3
Da der kritische Block mit "synchronisiert" geschützt ist, gibt es kein Problem. Bei mehreren Anrufen tritt ein (Leistungs-) Problem auf.
Mike Argyriou
17

Ich verwende ein SimpleDateFormat, um den XMLGregorianCalendar zu erstellen, wie in diesem Beispiel:

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));
}

Die erste Methode erstellt eine Instanz von XMLGregorianCalendar, die vom XML-Marshaller als gültiges xsd: date formatiert wird. Die zweite Methode führt zu einer gültigen xsd: dateTime.

Andrea Luciano
quelle
2

Sehr einfacher Weg zu mir. Formatieren von XMLGregorianCalendar für das Marshalling in Java.

Ich erstelle meine Daten einfach im guten Format. Das toStringwird genannt, um das gute Ergebnis zu erzielen.

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
quelle
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
quelle
0

Verwendung:

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
quelle