简体   繁体   English

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

[英]JAXB: Isn't it possible to use an XmlAdapter without @XmlJavaTypeAdapter?

Can't I register a bunch of XmlAdapter s to Marshaller | 我不能将一堆XmlAdapter注册到Marshaller | Unmarshaller so that I wouldn't need to specify @XmlJavaTypeAdapter on each filed, whose type isn't natively JAXB-supported? Unmarshaller所以我不需要在每个@XmlJavaTypeAdapter上指定@XmlJavaTypeAdapter ,其类型本身不支持JAXB?

I find it somewhat redundant . 我觉得有点多余

BTW, someMarshaller.setAdapter(...) seem not to do anything. 顺便说一句, someMarshaller.setAdapter(...)似乎没有做任何事情。

This is a quite a good question ! 这是一个非常好的问题!

The short answer is that no , using setAdapter on marshaller / unmarshaller does not mean that you don't have to use @XmlJavaTypeAdapter . 简短的回答是, ,在marshaller / unmarshaller上使用setAdapter并不意味着您不必使用@XmlJavaTypeAdapter

Let me explain this with a hypothetical (yet valid!) scenario. 让我用一个假设的(但有效的!)场景来解释这一点。

Consider in a web application, one fetches an event in the form of xml having following schema: 在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>

Equivalent to this, your model will look like: 相当于此,您的模型将如下所示:

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

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

Now the application is already having a bean called User which maintains the detailed information about the user. 现在,应用程序已经有一个名为User的bean,它维护有关用户的详细信息。

public class User {

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

    ..
}

Note that this User is not known to your JAXB Context. 请注意,您的JAXB上下文不知道此User For simplicity we have User as POJO, but it can be any Valid Java Class. 为简单起见,我们将User作为POJO,但它可以是任何Valid Java Class。

What application architect want is Event 's performedBy should be represented as User to gain full details. 应用程序架构师想要的是EventperformedBy应该表示为User以获取完整的详细信息。

Here is where @XmlJavaTypeAdapter comes into picture 这是@XmlJavaTypeAdapter进入图片的地方

JAXBContext is aware about performedBy as xs:string , but it has to be represented as User in memory in Java. JAXBContext而知道有关performedByxs:string ,但它必须被表示为User在Java内存。

Modified model looks like: 修改后的模型如下:

@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 {
             ..
     } 
}

The Adapter's definition says that - 适配器的定义说 -

  1. BoundType is User (In Memeory representation) BoundType是User(在Memeory表示中)
  2. ValueType is String (The data type JAXB Context is aware of) ValueType是String(JAXB Context知道的数据类型)

Coming to back to your question - 回到你的问题 -

I find it somewhat redundant. 我觉得有点多余。

BTW, someMarshaller.setAdapter(...) seem not to do anything. 顺便说一句,someMarshaller.setAdapter(...)似乎没有做任何事情。

Consider that our Adapter requires a class called UserContext in order to marshal / unmarshal sucessfully. 考虑到我们的适配器需要一个名为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);
     } 
}

Now the question is how will UserAdapter will fetch an instace of UserContext ?? 现在的问题是UserAdapter将如何获取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);
     } 
}

But JAXB Runtime can only accept Adapter with No-args constructor .. (Obviously JAXBContext does not know about application specific model) 但是JAXB Runtime只能接受带有No-args构造函数的Adapter ..(显然JAXBContext不知道特定于应用程序的模型)

So thankfully there is an option :D 谢天谢地,有一个选项:D

You can tell your unmarshaller to use given instance of UserAdapter rather than instating it by own its own. 您可以告诉您的unmarshaller使用给定的UserAdapter实例,而不是通过自己的实例来实现它。

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 method is available on both Marshaller & Unmarshaller MarshallerUnmarshaller都提供了setAdapter方法

Note: 注意:

  1. setAdapter on marshaller / unmarshaller does not mean that you don't have to use @XmlJavaTypeAdapter . marshaller / unmarshaller上的setAdapter并不意味着您不必使用@XmlJavaTypeAdapter

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

    If you omit this JAXB runtime has no clue that User is your Bound Type & Value Type is something else. 如果省略这个JAXB运行时不知道用户是你的绑定类型和值类型是别的。 It will try to marshal User as is & u will end up having wrong xml (or validation failure if enabled) 它将尝试按原样封送User ,并且最终会出现错误的xml(如果启用则验证失败)

  2. While we have taken a scenario where Adapter is required to be with arguments, hence use setAdapter method. 虽然我们已经采用了一个场景,其中Adapter需要带参数,因此使用setAdapter方法。

    Some adavanced usages are also there, where in even if you have default no-arg constructor, yet you provide an instance of the Adapter 一些高级用法也在那里,即使你有默认的no-arg构造函数,但是你提供了一个适配器的实例

    May this adapter is configured with data, which marshal / unmarshal operation is using ! 愿这个适配器配置数据,编组/解组操作正在使用!

You can use the package-info.java 您可以使用package-info.java

That's called "package level". 这被称为“包级别”。

Example : put a package-info.java in the same package that the class you want to marshall/unmarshall. 示例:将package-info.java放在要编组/取消编组的类所在的包中。

Suppose you have a class mypackage.model.A and an adapter CalendarAdapter in mypackage.adapter. 假设你在mypackage.adapter中有一个类mypackage.model.A和一个适配器CalendarAdapter。 Declare a package-info.java file in mypackage.model containing : 在mypackage.model中声明包含以下内容的package-info.java文件:

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

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

All field of type Calendar in the class A will be marshalled or unmarshalled with the CalendarAdapter. A类中所有类型的字段都将使用CalendarAdapter进行编组或解组。

You can find useful informations there : http://blog.bdoughan.com/2012/02/jaxb-and-package-level-xmladapters.html 您可以在那里找到有用的信息: http//blog.bdoughan.com/2012/02/jaxb-and-package-level-xmladapters.html

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM