[英]Why does JAXB need a no arg constructor for marshalling?
如果您嘗試封送引用沒有無參數構造函數的復雜類型的類,例如:
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
}
使用作為 Java 一部分的 JAXB 實現,如下所示:
Foo foo = new Foo();
JAXBContext jc = JAXBContext.newInstance(Foo.class);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Marshaller marshaller = jc.createMarshaller();
marshaller.marshal(foo, baos);
JAXB 將拋出一個
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions java.sql.Date does not have a no-arg default constructor
現在,我明白了為什么 JAXB 在解組時需要一個無參數構造函數 - 因為它需要實例化對象。 但是為什么 JAXB 在編組時需要一個無參數構造函數呢?
另外,另一個問題,如果字段為空,為什么 Java 的 JAXB 實現會拋出異常,並且無論如何都不會被編組?
我是否遺漏了什么,或者這些只是 Java 的 JAXB 實現中的錯誤實現選擇?
當JAXB (JSR-222)實現初始化其元數據時,它確保它可以支持編組和解組。
對於沒有無參數構造函數的 POJO 類,您可以使用類型級別的XmlAdapter
來處理它:
默認情況下不支持java.sql.Date
(盡管在EclipseLink JAXB (MOXy)中支持)。 這也可以使用通過@XmlJavaTypeAdapter
在字段、屬性或包級別指定的XmlAdapter
來處理:
另外,另一個問題,如果字段為空,為什么 Java 的 JAXB 實現會拋出異常,並且無論如何都不會被編組?
你看到了什么異常? 通常,當一個字段為 null 時,它不包含在 XML 結果中,除非它用@XmlElement(nillable=true)
注釋,在這種情況下元素將包含xsi:nil="true"
。
更新
您可以執行以下操作:
日期適配器
下面是一個XmlAdapter
,它將從您的 JAXB 實現不知道如何處理的java.sql.Date
轉換為它所做的java.util.Date
:
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());
}
}
富
XmlAdapter
通過@XmlJavaTypeAdapter
注解注冊:
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
}
回答您的問題:我認為這只是 JAXB(或者可能在 JAXB 實現中)中的糟糕設計。 無參數構造函數的存在在創建JAXBContext
期間得到驗證,因此無論您是想使用 JAXB 進行編組還是解組,都適用。 如果 JAXB 將這種類型的檢查推遲到JAXBContext.createUnmarshaller()
,那就太好了。 我認為深入研究這種設計是否真的由規范強制執行,或者它是否是 JAXB-RI 中的實現設計。
但確實有一個解決方法。
JAXB 實際上不需要用於編組的無參數構造函數。 在下文中,我將假設您僅使用 JAXB 進行編組,而不是解組。 我還假設您可以控制要編組的不可變對象,以便您可以更改它。 如果不是這種情況,那么前進的唯一方法是XmlAdapter
,如其他答案中所述。
假設您有一個類Customer
,它是一個不可變對象。 實例化是通過 Builder Pattern 或靜態方法進行的。
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 ...
}
確實,默認情況下您無法讓 JAXB 解組這樣的對象。 您將收到錯誤“ ....客戶沒有無參數的默認構造函數”。
至少有兩種方法可以解決這個問題。 它們都依賴於只放入一個方法或構造函數來使 JAXB 的自省愉快。
解決方案1
在這個方法中,我們告訴 JAXB 有一個靜態工廠方法可以用來實例化類的實例。 我們知道,但 JAXB 不知道,這確實永遠不會被使用。 訣竅是帶有factoryMethod
參數的@XmlType
注釋。 就是這樣:
@XmlType(factoryMethod="createInstanceJAXB")
public class Customer {
...
private static Customer createInstanceJAXB() { // makes JAXB happy, will never be invoked
return null; // ...therefore it doesn't matter what it returns
}
...
}
該方法是否如示例中那樣是私有的並不重要。 JAXB 仍然會接受它。 如果您將其設為私有,您的 IDE 會將該方法標記為未使用,但我仍然更喜歡私有。
解決方案2
在這個解決方案中,我們添加了一個私有的無參數構造函數,它只是將 null 傳遞給真正的構造函數。
public class Customer {
...
private Customer() { // makes JAXB happy, will never be invoked
this(null, null); // ...therefore it doesn't matter what it creates
}
...
}
構造函數是否如示例中那樣是私有的並不重要。 JAXB 仍然會接受它。
概括
這兩種解決方案都滿足了 JAXB 對無參數實例化的需求。 當我們自己知道我們只需要編組而不是解組時,我們需要這樣做是一種恥辱。
我不得不承認,我不知道在多大程度上這是一種僅適用於 JAXB-RI 而不適用於 EclipseLink MOXy 的 hack。 它絕對適用於 JAXB-RI。
您似乎認為 JAXB 內省代碼將具有用於初始化的特定於操作的路徑。 如果是這樣,那將導致大量重復代碼並且將是一個糟糕的實現。 我想 JAXB 代碼有一個通用例程,它在第一次需要模型類時檢查它並驗證它是否遵循所有必要的約定。 在這種情況下,它會失敗,因為其中一個成員沒有所需的無參數構造函數。 初始化邏輯很可能不是特定於編組/解組的,也極不可能考慮當前對象實例。
一些企業和依賴注入框架使用反射Class.newInstance()來創建類的新實例。 此方法需要公共無參數構造函數才能實例化對象。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.