Wenn Sie versuchen, eine Klasse zu marshallen, die auf einen komplexen Typ verweist, der keinen No-Arg-Konstruktor enthält, z.
import Java.sql.Date;
@XmlRootElement(name = "Foo")
@XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
int i;
Date d; //Java.sql.Date does not have a no-arg constructor
}
mit der JAXB-Implementierung, die Teil von Java ist, wie folgt:
Foo foo = new Foo();
JAXBContext jc = JAXBContext.newInstance(Foo.class);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Marshaller marshaller = jc.createMarshaller();
marshaller.marshal(foo, baos);
JAXB wirft ein
com.Sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions Java.sql.Date does not have a no-arg default constructor
Jetzt verstehe ich, warum JAXB einen No-Arg-Konstruktor für das Unmarshalling benötigt - weil er das Objekt instanziieren muss. Aber warum braucht JAXB beim Rangieren einen No-Arg-Konstruktor?
Außerdem, warum, löst die JAXB-Implementierung von Java eine Ausnahme aus, wenn das Feld null ist und nicht sowieso gemarshallt wird?
Fehle ich etwas oder sind das nur schlechte Implementierungsentscheidungen in Javas JAXB-Implementierung?
Wenn eine Implementierung von JAXB (JSR-222) ihre Metadaten initialisiert, wird sichergestellt, dass sie sowohl das Marshalling als auch das De-Marshalling unterstützt.
Für POJO-Klassen, die keinen no-arg-Konstruktor haben, können Sie eine Typebene XmlAdapter
verwenden, um damit umzugehen:
Java.sql.Date
wird standardmäßig nicht unterstützt (obwohl in EclipseLink JAXB (MOXy) ist). Dies kann auch mit einer XmlAdapter
behandelt werden, die über @XmlJavaTypeAdapter
auf Feld-, Eigenschafts- oder Paketebene angegeben wird:
Außerdem, warum, wirft Javas JAXB-Implementierung eine Ausnahme, wenn das Feld NULL ist und nicht gemarshallt wird sowieso?
Welche Ausnahme sehen Sie? Normalerweise ist ein Feld, wenn es null ist, nicht im XML-Ergebnis enthalten, es sei denn, es ist mit @XmlElement(nillable=true)
versehen. In diesem Fall enthält das Element xsi:nil="true"
.
UPDATE
Sie könnten folgendes tun:
SqlDateAdapter
Nachfolgend finden Sie eine XmlAdapter
, die aus dem Java.sql.Date
, den Ihre JAXB-Implementierung nicht kennt, in einen Java.util.Date
umzuwandeln ist.
package forum9268074;
import javax.xml.bind.annotation.adapters.*;
public class SqlDateAdapter extends XmlAdapter<Java.util.Date, Java.sql.Date> {
@Override
public Java.util.Date marshal(Java.sql.Date sqlDate) throws Exception {
if(null == sqlDate) {
return null;
}
return new Java.util.Date(sqlDate.getTime());
}
@Override
public Java.sql.Date unmarshal(Java.util.Date utilDate) throws Exception {
if(null == utilDate) {
return null;
}
return new Java.sql.Date(utilDate.getTime());
}
}
Foo
Die Variable XmlAdapter
wird über die Annotation @XmlJavaTypeAdapter
registriert:
package forum9268074;
import Java.sql.Date;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlRootElement(name = "Foo")
@XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
int i;
@XmlJavaTypeAdapter(SqlDateAdapter.class)
Date d; //Java.sql.Date does not have a no-arg constructor
}
Um Ihre Frage zu beantworten: Ich denke, das ist nur ein schlechtes Design in JAXB (oder vielleicht in JAXB-Implementierungen). Das Vorhandensein eines No-Arg-Konstruktors wird beim Erstellen der Variable JAXBContext
überprüft und gilt daher unabhängig davon, ob Sie JAXB für das Marshalling oder das Aufheben von Marshalls verwenden möchten. Es wäre schön gewesen, wenn JAXB diese Art der Überprüfung auf JAXBContext.createUnmarshaller()
verschieben würde. Ich denke, es wäre interessant, sich mit Dig auseinanderzusetzen, ob dieses Design tatsächlich von der Spezifikation vorgegeben wird oder ob es sich um ein Implementierungsdesign in JAXB-RI handelt.
Es gibt jedoch einen Workaround.
JAXB benötigt eigentlich keinen No-Arg-Konstruktor für das Marshalling. Im Folgenden gehe ich davon aus, dass Sie JAXB ausschließlich zum Rangieren und nicht zum Rangieren verwenden. Ich gehe außerdem davon aus, dass Sie die Kontrolle über das unveränderliche Objekt haben, das Sie zusammenstellen möchten, damit Sie es ändern können. Wenn dies nicht der Fall ist, ist der einzige Weg nach vorne XmlAdapter
, wie in anderen Antworten beschrieben.
Angenommen, Sie haben eine Klasse, Customer
, die ein unveränderliches Objekt ist. Die Instantiierung erfolgt über Builder Pattern oder statische Methoden.
public class Customer {
private final String firstName;
private final String lastName;
private Customer(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
// Object created via builder pattern
public static CustomerBuilder createBuilder() {
...
}
// getters here ...
}
Es stimmt, dass Sie standardmäßig JAXB nicht dazu auffordern können, ein solches Objekt zu entfernen. Sie erhalten die Fehlermeldung ".... Der Kunde hat keinen Standardkonstruktor no-arg".
Es gibt mindestens zwei Möglichkeiten, dies zu lösen. Beide sind darauf angewiesen, eine Methode oder einen Konstruktor einzubauen, um die Selbstbeobachtung von JAXB glücklich zu machen.
Lösung 1
In dieser Methode teilen wir JAXB mit, dass es eine statische Factory-Methode gibt, mit der eine Instanz der Klasse instanziiert werden kann. Wir wissen, aber JAXB weiß nicht, dass dies niemals verwendet wird. Der Trick ist der @XmlType
ANNOTATION WITH factoryMethod
Parameter. Hier ist wie:
@XmlType(factoryMethod="createInstanceJAXB")
public class Customer {
...
private static CustomerImpl createInstanceJAXB() { // makes JAXB happy, will never be invoked
return null; // ...therefore it doesn't matter what it returns
}
...
}
Es ist egal, ob die Methode wie im Beispiel privat ist. JAXB wird es immer noch akzeptieren. Ihre IDE kennzeichnet die Methode als nicht verwendet, wenn Sie sie als privat definieren, aber ich bevorzuge privat.
Lösung 2
In dieser Lösung fügen wir einen privaten no-arg-Konstruktor hinzu, der einfach null an den echten Konstruktor übergibt.
public class Customer {
...
private Customer() { // makes JAXB happy, will never be invoked
this(null, null); // ...therefore it doesn't matter what it creates
}
...
}
Es ist egal, ob der Konstruktor wie im Beispiel privat ist. JAXB wird es immer noch akzeptieren.
Zusammenfassung
Beide Lösungen erfüllen den Wunsch von JAXB nach einer Instantiierung ohne Argumente. Es ist eine Schande, dass wir dies tun müssen, wenn wir selbst wissen, dass wir nur Marshalling betreiben müssen, nicht Unmarschall.
Ich muss zugeben, dass ich nicht weiß, in welchem Umfang dies ein Hack ist, der nur mit JAXB-RI und nicht beispielsweise mit EclipseLink MOXy funktioniert. Es funktioniert definitiv mit JAXB-RI.
Sie haben scheinbar den Eindruck, dass der JAXB-Introspektionscode aktionsspezifische Pfade für die Initialisierung hat. Wenn dies der Fall ist, führt dies zu einer Vielzahl von doppeltem Code und wäre eine schlechte Implementierung. Ich könnte mir vorstellen, dass der JAXB-Code eine gemeinsame Routine hat, die eine Modellklasse beim ersten Mal untersucht und bestätigt, dass sie allen notwendigen Konventionen folgt. In dieser Situation schlägt es fehl, weil eines der Member nicht den erforderlichen No-Arg-Konstruktor hat. Die Initialisierungslogik ist höchstwahrscheinlich nicht spezifisch für Marshalls/Unmarshalls und es ist auch höchst unwahrscheinlich, dass sie die aktuelle Objektinstanz berücksichtigt.