JAXB: нельзя ли использовать XmlAdapter без @XmlJavaTypeAdapter? - PullRequest
18 голосов
/ 28 июля 2011

Не могу ли я зарегистрировать группу от XmlAdapter до Marshaller | Unmarshaller, чтобы мне не нужно было указывать @XmlJavaTypeAdapter для каждого поля, тип которого изначально не поддерживается JAXB?

Я нахожу это несколько избыточным .

Кстати, someMarshaller.setAdapter(...), похоже, ничего не делает.

Ответы [ 2 ]

23 голосов
/ 04 января 2012

Это довольно хороший вопрос!

Короткий ответ: нет , использование setAdapter на маршаллере / демаршаллере не означает, что вам не нужно использовать @XmlJavaTypeAdapter.

Позвольте мне объяснить это гипотетическим (пока действительным!) Сценарием.

Рассмотрим в веб-приложении получение события в форме 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, который поддерживает детальный информация о пользователе.

public class User {

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

    ..
}

Обратите внимание, что это User неизвестно вашему контексту JAXB. Для простоты у нас есть User как POJO, но это может быть любой Valid Java Class.

Что архитектор приложений хочет, чтобы Event performedBy было представлено как User чтобы получить полную информацию.

Вот где @XmlJavaTypeAdapter входит в картинку

JAXBContext знает о performedBy как xs:string, но он должен быть представлен как User в памяти на Java.

Модифицированная модель выглядит так:

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

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

UserAdapter.java:

public class UserAdapter extends XmlAdapter<String, User> {

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

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

Определение Адаптера говорит, что -

  1. BoundType - пользователь (в представлении Memeory)
  2. ValueType is String (Тип данных, о котором осведомлен JAXB-контекст)

Возвращаясь к вашему вопросу -

Я считаю это несколько избыточным.

Кстати, someMarshaller.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 ?? В качестве хорошего дизайна его всегда следует поставлять, пока он создан.

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 Runtime может принимать только адаптер с конструктором без аргументов. (Очевидно, JAXBContext не знает о конкретной модели приложения)

Так что, к счастью, есть опция: D

Вы можете указать своему 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 метод доступен для обоих Marshaller & Unmarshaller

Примечание:

  1. setAdapter на маршаллере / демаршаллере не означает, что вам не нужно использовать @XmlJavaTypeAdapter.

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

    Если вы пропустите это время выполнения JAXB, вы не поймете, что User - это ваш тип привязки, а тип значения - это нечто другое. Он попытается маршалировать User как есть, и у вас будет неправильный xml (или ошибка проверки, если включена)

  2. В то время как мы взяли сценарий, в котором Адаптер должен быть с аргументами, отсюда используйте метод setAdapter.

    Также есть некоторые расширенные применения, даже если у вас есть конструктор по умолчанию без аргументов, но вы предоставляете экземпляр адаптера

    Может этот адаптер сконфигурирован с данными, которые использует маршал / демаршал!

7 голосов
/ 25 ноября 2014

Вы можете использовать package-info.java

Это называется «уровень пакета».

Пример: поместить package-info.java в тот же пакет, что и класс, который вы хотитеmarshall / unmarshall.

Предположим, у вас есть класс mypackage.model.A и адаптер CalendarAdapter в mypackage.adapter.Объявите файл package-info.java в mypackage.model, содержащий:

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

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

Все поля типа Calendar в классе A будут маршалированы или демаршалированы с помощью CalendarAdapter.там полезной информации: http://blog.bdoughan.com/2012/02/jaxb-and-package-level-xmladapters.html

...