JAXB:如果没有@XmlJavaTypeAdapter,是不是可以使用XmlAdapter?

时间:2011-07-28 10:01:54

标签: java jaxb adapter

我无法向XmlAdapter | Marshaller注册一堆Unmarshaller,因此我不需要在每个字段上指定@XmlJavaTypeAdapter,其类型为原生JAXB支持吗?

我发现它有些redundant

BTW,someMarshaller.setAdapter(...)似乎没有做任何事情。

2 个答案:

答案 0 :(得分:22)

这是一个非常好的问题!

简短的回答是没有,在marshaller / unmarshaller上使用setAdapter并不意味着您不必使用@XmlJavaTypeAdapter

让我用一个假设的(但有效的!)场景来解释这个。

在Web应用程序中考虑,以xml的形式获取具有以下模式的事件:

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

相当于此,您的模型将如下所示:

@XmlRootElement(name="event")
@XmlType(name="")
public class Event {

     @XmlElement(required=true)
     protected String performedBy;
}

现在,应用程序已经有了一个名为User的bean来维护详细信息 有关用户的信息。

public class User {

    private String id;
    private String firstName;
    private String lastName;

    ..
}

请注意,JAXB Context不知道此User。 为简单起见,我们将User作为POJO,但它可以是任何Valid Java Class。

应用程序架构师想要的是Event的{​​{1}}应该表示为performedBy 获得全部细节。

这是@XmlJavaTypeAdapter进入图片的地方

JAXBContext将User视为performedBy,但必须将其表示为 Java中的xs:string在内存中。

修改后的模型如下:

User

UserAdapter.java:

@XmlRootElement(name="event")
@XmlType(name="")
public class Event {

     @XmlElement(required=true)
     @XmlJavaTypeAdapter(UserAdapter.class) 
     protected User performedBy;
}

适配器的定义是 -

  1. BoundType是User(在Memeory表示中)
  2. ValueType是String(JAXB Context知道的数据类型)
  3. 回到你的问题 -

      

    我发现它有些多余。

         顺便说一句,someMarshaller.setAdapter(...)似乎没有做任何事情。

    考虑到我们的适配器需要一个名为UserContext的类才能成功编组/解组。

    public class UserAdapter extends XmlAdapter<String, User> {
    
         public String marshal(User boundType) throws   Exception {
                 ..   
         } 
    
         public User unmarshal(String valueType) throws Exception {
                 ..
         } 
    }
    

    现在的问题是UserAdapter将如何获取UserContext的实例? 作为一个好的设计,应该在实例化时提供它。

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

    但是JAXB Runtime只能接受带有No-args构造函数的Adapter。 (显然JAXBContext不了解应用程序特定的模型)

    谢天谢地,有一个选项:D

    您可以告诉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); } } 使用unmarshaller的给定实例,而不是通过自己的实例来设置它。

    UserAdapter
    {p} 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&amp; Marshaller

    注意:

      关于marshaller / unmarshaller的
    1. Unmarshaller并不意味着您不必使用setAdapter

      @XmlJavaTypeAdapter

      如果省略这个JAXB运行时不知道用户是你的绑定类型&amp;价值类型是另一回事。它将尝试按原样排列@XmlRootElement(name="event") @XmlType(name="") public class Event { @XmlElement(required=true) // @XmlJavaTypeAdapter(UserAdapter.class) protected User performedBy; } 。你最终会得到错误的xml (或启用验证失败)

    2. 虽然我们已经采用了一个场景,其中必须使用Adapter作为参数 使用User方法。

      一些高级用法也在那里,即使你有默认的no-arg构造函数,但是你提供了一个适配器的实例

      这个适配器可以配置数据,编组/解组操作正在使用!

答案 1 :(得分:6)

您可以使用package-info.java

这称为“包级别”。

示例: 将package-info.java放在要编组/解组的类所在的包中。

假设您在mypackage.adapter中有一个类mypackage.model.A和一个适配器CalendarAdapter。 在mypackage.model中声明包含以下内容的package-info.java文件:

@XmlJavaTypeAdapters({
    @XmlJavaTypeAdapter(value = CalendarAdapter.class, type = Calendar.class)
})
package mypackage.model;

import java.util.Calendar;
import mypackage.adapter.CalendarAdapter;

A类中所有类型日历的字段都将使用CalendarAdapter进行编组或解组。

你可以在那里找到有用的信息: http://blog.bdoughan.com/2012/02/jaxb-and-package-level-xmladapters.html