JAXB:难道不能在没有@XmlJavaTypeAdapter的情况下使用XmlAdapter吗?

2022-09-01 20:51:06

我不能注册一堆s来|这样我就不需要在每个文件上指定,其类型本身不受 JAXB 支持?XmlAdapterMarshallerUnmarshaller@XmlJavaTypeAdapter

我发现它有些多余

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


答案 1

这是一个非常好的问题!

简短的回答是不,在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;
}

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

public class User {

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

    ..
}

请注意,JAXB 上下文不知道这一点。为简单起见,我们将 User 作为 POJO,但它可以是任何有效的 Java 类。User

应用程序架构师想要的是,应该表示为获得完整的细节。EventperformedByUser

这是@XmlJavaTypeAdapter进入画面的地方

JAXBContext 知道 as,但它必须在 Java 中的内存中表示。performedByxs:stringUser

修改后的模型如下所示:

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

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

用户适配器.java:

public class UserAdapter extends XmlAdapter<String, User> {

     public String marshal(User boundType) throws   Exception {
             ..   
     } 

     public User unmarshal(String valueType) throws Exception {
             ..
     } 
}

适配器的定义是 -

  1. BoundType 是 User(在 Memeory 表示形式中)
  2. 值类型为字符串(JAXB 上下文感知的数据类型)

回到你的问题 -

我发现它有些多余。

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

考虑到我们的适配器需要一个名为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);
     } 
}

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

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 运行时只能接受带有 No-args 构造函数的适配器。(显然JAXBContext不知道特定于应用程序的模型)

所以值得庆幸的是,有一个选择:D

你可以告诉你使用给定的实例,而不是自己陈述它。unmarshallerUserAdapter

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

注意:

  1. setAdapter在 marshaller / unmarshaller 上并不意味着您不必使用 .@XmlJavaTypeAdapter

    @XmlRootElement(name="event")
    @XmlType(name="")
    public class Event {
    
        @XmlElement(required=true)
        // @XmlJavaTypeAdapter(UserAdapter.class) 
        protected User performedBy;
    }
    

    如果你省略了这个 JAXB 运行时,就不知道用户是你的绑定类型,值类型是别的什么。它将尝试按原样进行封送处理,您最终将拥有错误的xml(如果启用,则验证失败)User

  2. 虽然我们采用了一个场景,其中适配器需要与参数一起使用,因此使用方法。setAdapter

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

    可能此适配器配置了数据,哪个封送/取消元帅操作正在使用!


答案 2

您可以使用包信息.java

这称为“包级别”。

示例:将一个包信息.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 中所有“日历”类型的字段都将使用“日历适配器”进行编组或取消编组。

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


推荐