rou*_*ble 45 java xml jaxb marshalling
如果您尝试编组一个引用没有no-arg构造函数的复杂类型的类,例如:
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
}
Run Code Online (Sandbox Code Playgroud)
使用JAXB实现作为Java的一部分,如下所示:
Foo foo = new Foo();
JAXBContext jc = JAXBContext.newInstance(Foo.class);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Marshaller marshaller = jc.createMarshaller();
marshaller.marshal(foo, baos);
Run Code Online (Sandbox Code Playgroud)
JAXB将抛出一个
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions java.sql.Date does not have a no-arg default constructor
Run Code Online (Sandbox Code Playgroud)
现在,我理解为什么JAXB在解组时需要一个无参数的构造函数 - 因为它需要实例化对象.但是为什么JAXB在编组时需要一个无参数的构造函数?
另外,为什么Java的JAXB实现如果该字段为空则抛出异常,并且无论如何都不会被编组?
我在Java的JAXB实现中遗漏了一些东西,或者这些只是糟糕的实现选择?
bdo*_*han 25
当JAXB(JSR-222)实现初始化其元数据时,它确保它可以支持编组和解组.
对于没有no-arg构造函数的POJO类,您可以使用类型级别XmlAdapter来处理它:
java.sql.Date默认情况下不支持(尽管在EclipseLink JAXB(MOXy)中).这也可以使用字段,属性或包级别的XmlAdapter指定via 来处理@XmlJavaTypeAdapter:
另外,为什么Java的JAXB实现如果该字段为空则抛出异常,并且无论如何都不会被编组?
你看到什么例外?通常,当一个字段为空时,它不包含在XML结果中,除非它被注释,@XmlElement(nillable=true)在这种情况下该元素将包括xsi:nil="true".
UPDATE
您可以执行以下操作:
SqlDateAdapter
下面是一个XmlAdapter将java.sql.Date您的JAXB实现不知道如何处理的转换为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());
}
}
Run Code Online (Sandbox Code Playgroud)
富
将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
}
Run Code Online (Sandbox Code Playgroud)
回答您的问题:我认为这在JAXB(或者也许在JAXB实现中)只是糟糕的设计。no-arg构造函数的存在在创建时会得到验证JAXBContext,因此无论您想使用JAXB进行编组还是反编组,都适用。如果JAXB将这种类型的检查推迟到,那将是很棒的JAXBContext.createUnmarshaller()。我认为,深入研究该设计是否真正由规范来强制执行,或者它是否是JAXB-RI中的实现设计,将会很有趣。
但是确实有解决方法。
JAXB实际上不需要无参数构造函数来进行编组。在下面的内容中,我假设您仅将JAXB用于编组而不是编组。我还假定您可以控制要编组的不可变对象,以便可以对其进行更改。如果不是这种情况,那么唯一的解决方法就是XmlAdapter其他答案中所述。
假设您有一个类,Customer这是一个不可变的对象。实例化是通过构建器模式或静态方法进行的。
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 ...
}
Run Code Online (Sandbox Code Playgroud)
是的,默认情况下,您无法使JAXB解组此类对象。您将收到错误“ .... Customer没有无参数的默认构造函数 ”。
至少有两种方法可以解决此问题。它们都依赖于仅使用方法或构造函数来使JAXB的自省快乐。
解决方案1
在此方法中,我们告诉JAXB,有一个静态工厂方法可用于实例化该类的实例。我们知道,但是JAXB并不知道,这确实将永远不会被使用。诀窍是@XmlType带有factoryMethod参数的注释。这是如何做:
@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
}
...
}
Run Code Online (Sandbox Code Playgroud)
如示例中所述方法是否私有并不重要。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
}
...
}
Run Code Online (Sandbox Code Playgroud)
如示例中的构造函数是否私有并不重要。JAXB仍然会接受。
摘要
两种解决方案都满足JAXB对无参数实例化的需求。当我们自己知道我们只需要元帅而不是元帅时,我们就必须这样做,真是可惜。
我不得不承认,我不知道这是什么程度的hack,仅适用于JAXB-RI,不适用于EclipseLink MOXy。它绝对适用于JAXB-RI。
| 归档时间: |
|
| 查看次数: |
45630 次 |
| 最近记录: |