JAXB:没有@XmlJavaTypeAdapter 就不能使用XmlAdapter 吗?
我不能将一堆 XmlAdapter
注册到 Marshaller
|Unmarshaller
以便我不需要指定 @XmlJavaTypeAdapter
在每个字段上,其类型本身不支持 JAXB?
Can't I register a bunch of XmlAdapter
s to Marshaller
|Unmarshaller
so that I wouldn't need to specify @XmlJavaTypeAdapter
on each filed, whose type isn't natively JAXB-supported?
我觉得它有点冗余.
顺便说一句,someMarshaller.setAdapter(...)
似乎什么也没做.
BTW, someMarshaller.setAdapter(...)
seem not to do anything.
推荐答案
这是一个相当不错的问题!
This is a quite a good question !
简短的回答是 no,在 marshaller/unmarshaller 上使用 setAdapter
并不意味着您不必使用 @XmlJavaTypeAdapter
.
The short answer is that no, using setAdapter
on marshaller / unmarshaller does not mean that you don't have to use @XmlJavaTypeAdapter
.
让我用一个假设的(但有效的!)场景来解释这一点.
Let me explain this with a hypothetical (yet valid!) scenario.
考虑在一个 Web 应用程序中,以具有以下模式的 xml 形式获取事件:
Consider in a web application, one fetches an event in the form of xml having following schema:
<xs:element name="event" >
<xs:complexType>
<xs:sequence>
<!-- Avoiding other elements for concentrating on our adapter -->
<xs:element name="performedBy" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
与此等效,您的模型将如下所示:
Equivalent to this, your model will look like:
@XmlRootElement(name="event")
@XmlType(name="")
public class Event {
@XmlElement(required=true)
protected String performedBy;
}
现在应用程序已经有了一个名为 User
的 bean,它维护了详细信息用户信息.
Now the application is already having a bean called User
which maintains the detailed
information about the user.
public class User {
private String id;
private String firstName;
private String lastName;
..
}
请注意,您的 JAXB 上下文不知道此 User
.为简单起见,我们将 User 作为 POJO,但它可以是任何有效的 Java 类.
Note that this User
is not known to your JAXB Context.
For simplicity we have User as POJO, but it can be any Valid Java Class.
应用程序架构师想要的是 Event
的 performedBy
应该表示为 User
以获得完整的细节.
What application architect want is Event
's performedBy
should be represented as User
to gain full details.
这就是@XmlJavaTypeAdapter 出现的地方
Here is where @XmlJavaTypeAdapter comes into picture
JAXBContext 知道 performedBy
为 xs:string
,但它必须表示为User
在 Java 中的内存中.
JAXBContext is aware about performedBy
as xs:string
, but it has to be represented as
User
in memory in Java.
修改后的模型如下:
@XmlRootElement(name="event")
@XmlType(name="")
public class Event {
@XmlElement(required=true)
@XmlJavaTypeAdapter(UserAdapter.class)
protected User performedBy;
}
UserAdapter.java:
UserAdapter.java:
public class UserAdapter extends XmlAdapter<String, User> {
public String marshal(User boundType) throws Exception {
..
}
public User unmarshal(String valueType) throws Exception {
..
}
}
适配器的定义说 -
- BoundType 是 User(在内存中表示)
- ValueType 是 String(JAXB 上下文知道的数据类型)
回到您的问题 -
我觉得有点多余.
顺便说一句,someMarshaller.setAdapter(...) 似乎什么也没做.
BTW, someMarshaller.setAdapter(...) seem not to do anything.
考虑到我们的适配器需要一个名为 UserContext 的类才能成功编组/解组.
Consider that our Adapter requires a class called UserContext in order to marshal / unmarshal sucessfully.
public class UserAdapter extends XmlAdapter<String, User> {
private UserContext userContext;
public String marshal(User boundType) throws Exception {
return boundType.getId();
}
public User unmarshal(String valueType) throws Exception {
return userContext.findUserById(valueType);
}
}
现在的问题是 UserAdapter 将如何获取 UserContext 的实例?作为一个好的设计,应该总是在实例化时提供它..
Now the question is how will UserAdapter will fetch an instace of UserContext ?? As a good design one should always supply it while it has got instantiated ..
public class UserAdapter extends XmlAdapter<String, User> {
private UserContext userContext;
public UserAdapter(UserContext userContext) {
this.userContext = userContext;
}
public String marshal(User boundType) throws Exception {
return boundType.getId();
}
public User unmarshal(String valueType) throws Exception {
return userContext.findUserById(valueType);
}
}
但是 JAXB 运行时只能接受带有无参数构造函数的适配器..(显然 JAXBContext 不知道应用程序特定的模型)
But JAXB Runtime can only accept Adapter with No-args constructor .. (Obviously JAXBContext does not know about application specific model)
幸好有一个选项:D
您可以告诉您的 unmarshaller
使用给定的 UserAdapter
实例,而不是自己安装它.
You can tell your unmarshaller
to use given instance of UserAdapter
rather than instating it by own its own.
public class Test {
public static void main(String... args) {
JAXBContext context = JAXBContext.getInstance(Event.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
UserContext userContext = null; // fetch it from some where
unmarshaller.setAdapter(UserAdapter.class, new UserAdapter(userContext));
Event event = (Event) unmarshaller.unmarshal(..);
}
}
setAdapter
方法在 Marshaller
和解组器
注意:
setAdapter
on marshaller/unmarshaller 并不意味着你不必使用@XmlJavaTypeAdapter
.
setAdapter
on marshaller / unmarshaller does not mean that you don't have to use@XmlJavaTypeAdapter
.
@XmlRootElement(name="event")
@XmlType(name="")
public class Event {
@XmlElement(required=true)
// @XmlJavaTypeAdapter(UserAdapter.class)
protected User performedBy;
}
如果您省略此 JAXB 运行时,则不知道 User 是您的绑定类型 &值类型是另一回事.它将尝试按原样编组 User
你最终会得到错误的xml(如果启用,则验证失败)
If you omit this JAXB runtime has no clue that User is your Bound Type & Value Type is something else. It will try to marshal User
as is & u will end up having wrong xml
(or validation failure if enabled)
虽然我们已经采用了适配器需要带有参数的场景,因此使用 setAdapter
方法.
While we have taken a scenario where Adapter is required to be with arguments, hence
use setAdapter
method.
还有一些高级用法,即使你有默认的无参数构造函数,但你提供了一个适配器实例
Some adavanced usages are also there, where in even if you have default no-arg constructor, yet you provide an instance of the Adapter
可能这个适配器配置了数据,哪个编组/解组操作正在使用!
May this adapter is configured with data, which marshal / unmarshal operation is using !
相关文章