«1. Обзор

В этом учебном пособии представлено введение в привязку данных Aegis, подсистему, которая может отображать объекты Java и XML-документы, описываемые XML-схемами. Aegis позволяет детально контролировать процесс картирования, сводя к минимуму усилия по программированию.

Aegis является частью Apache CXF, но его использование не ограничивается только этой структурой. Вместо этого этот механизм привязки данных можно использовать где угодно, поэтому в этом руководстве мы сосредоточимся на его использовании в качестве независимой подсистемы.

2. Зависимости Maven

Единственная зависимость, необходимая для активации привязки данных Aegis:

<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-databinding-aegis</artifactId>
    <version>3.1.8</version>
</dependency>

Последнюю версию этого артефакта можно найти здесь.

3. Определения типов

В этом разделе рассматриваются определения трех типов, используемых для иллюстрации Aegis.

3.1. Курс

Это самый простой класс в нашем примере, который определяется как:

public class Course {
    private int id;
    private String name;
    private String instructor;
    private Date enrolmentDate;

    // standard getters and setters
}

3.2. CourseRepo

CourseRepo — это тип верхнего уровня в нашей модели. Мы определяем его как интерфейс, а не класс, чтобы продемонстрировать, как легко маршалировать интерфейс Java, что невозможно в JAXB без специального адаптера:

public interface CourseRepo {
    String getGreeting();
    void setGreeting(String greeting);
    Map<Integer, Course> getCourses();
    void setCourses(Map<Integer, Course> courses);
    void addCourse(Course course);  
}

Обратите внимание, что мы объявляем метод getCourses с возвращаемым типом Map . Это намеренно, чтобы выразить еще одно преимущество Aegis над JAXB. Последний не может маршалировать карту без специального адаптера, в то время как первый может.

3.3. CourseRepoImpl

Этот класс обеспечивает реализацию интерфейса CourseRepo:

public class CourseRepoImpl implements CourseRepo {
    private String greeting;
    private Map<Integer, Course> courses = new HashMap<>();

    // standard getters and setters

    @Override
    public void addCourse(Course course) {
        courses.put(course.getId(), course);
    }
}

4. Пользовательское связывание данных

Чтобы настройка вступила в силу, в пути к классам должны присутствовать файлы сопоставления XML. Необходимо, чтобы эти файлы были помещены в каталог, структура которого соответствует иерархии пакетов соответствующих типов Java.

Например, если класс с полным именем называется package.ClassName, связанный с ним файл сопоставления должен находиться в подкаталоге package/ClassName пути к классам. Имя файла сопоставления должно совпадать с соответствующим типом Java с добавленным к нему суффиксом .aegis.xml.

4.1. Сопоставление CourseRepo

Интерфейс CourseRepo принадлежит пакету com.baeldung.cxf.aegis, поэтому соответствующий файл сопоставления называется CourseRepo.aegis.xml и помещается в каталог com/baeldung/cxf/aegis в пути к классам.

В файле сопоставления CourseRepo мы меняем имя и пространство имен XML-элемента, связанного с интерфейсом CourseRepo, а также стиль его свойства приветствия:

<mappings xmlns:ns="http://courserepo.baeldung.com">
    <mapping name="ns:Baeldung">
        <property name="greeting" style="attribute"/>
    </mapping>
</mappings>

4.2. Сопоставление курса

Подобно типу CourseRepo, файл сопоставления класса Course называется Course.aegis.xml и также находится в каталоге com/baeldung/cxf/aegis.

В этом файле сопоставления мы указываем Aegis игнорировать свойство инструктора класса Course при маршалинге, чтобы его значение было недоступно в объекте, воссозданном из выходного XML-документа:

<mappings>
    <mapping>
        <property name="instructor" ignore="true"/>
    </mapping>
</mappings>

Домашняя страница Aegis здесь мы можем найти больше вариантов настройки.

5. Тестирование

Этот раздел представляет собой пошаговое руководство по настройке и выполнению тестового примера, иллюстрирующего использование привязок данных Aegis.

Чтобы упростить процесс тестирования, мы объявляем два поля в тестовом классе:

public class BaeldungTest {
    private AegisContext context;
    private String fileName = "baeldung.xml";

    // other methods
}

Эти поля были определены здесь для использования другими методами этого класса.

5.1. Инициализация AegisContext

Сначала необходимо создать объект AegisContext:

context = new AegisContext();

Этот экземпляр AegisContext затем настраивается и инициализируется. Вот как мы устанавливаем корневые классы для контекста:

Set<Type> rootClasses = new HashSet<Type>();
rootClasses.add(CourseRepo.class);
context.setRootClasses(rootClasses);

Aegis создает элемент сопоставления XML для каждого типа в объекте Set\u003cType\u003e. В этом руководстве мы устанавливаем только CourseRepo в качестве корневого типа.

Теперь давайте установим карту реализации для контекста, чтобы указать прокси-класс для интерфейса CourseRepo:

Map<Class<?>, String> beanImplementationMap = new HashMap<>();
beanImplementationMap.put(CourseRepoImpl.class, "CourseRepo");
context.setBeanImplementationMap(beanImplementationMap);

Последняя конфигурация для контекста Aegis говорит ему установить атрибут xsi:type в соответствующем XML документ. Этот атрибут содержит фактическое имя типа связанного объекта Java, если только он не переопределен файлом сопоставления:

context.setWriteXsiTypes(true);

Теперь наш экземпляр AegisContext готов к инициализации:

context.initialize();

«

private void initializeContext() {
    // ...
}

«Чтобы код оставался чистым, мы собираем все фрагменты кода из этого подраздела в один вспомогательный метод:

5.2. Простая настройка данных

private CourseRepoImpl initCourseRepo() {
    Course restCourse = new Course();
    restCourse.setId(1);
    restCourse.setName("REST with Spring");
    restCourse.setInstructor("Eugen");
    restCourse.setEnrolmentDate(new Date(1234567890000L));
    
    Course securityCourse = new Course();
    securityCourse.setId(2);
    securityCourse.setName("Learn Spring Security");
    securityCourse.setInstructor("Eugen");
    securityCourse.setEnrolmentDate(new Date(1456789000000L));
    
    CourseRepoImpl courseRepo = new CourseRepoImpl();
    courseRepo.setGreeting("Welcome to Beldung!");
    courseRepo.addCourse(restCourse);
    courseRepo.addCourse(securityCourse);
    return courseRepo;
}

Из-за простоты этого руководства мы генерируем образцы данных прямо в памяти, а не полагаемся на постоянное решение. Давайте заполним репозиторий курса, используя приведенную ниже логику настройки:

5.3. Связывание объектов Java и элементов XML

private void marshalCourseRepo(CourseRepo courseRepo) throws Exception {
    AegisWriter<XMLStreamWriter> writer = context.createXMLStreamWriter();
    AegisType aegisType = context.getTypeMapping().getType(CourseRepo.class);
    XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance()
      .createXMLStreamWriter(new FileOutputStream(fileName));
    
    writer.write(courseRepo, 
      new QName("http://aegis.cxf.baeldung.com", "baeldung"), false, xmlWriter, aegisType);
    
    xmlWriter.close();
}

Шаги, которые необходимо предпринять для маршалирования объектов Java в элементы XML, иллюстрируются следующим вспомогательным методом:

Как мы видим, объекты AegisWriter и AegisType должны быть созданы. из экземпляра AegisContext. Затем объект AegisWriter маршалирует данный экземпляр Java к указанному выходу.

В данном случае это объект XMLStreamWriter, связанный с файлом, названным в соответствии со значением поля уровня класса fileName в файловой системе.

private CourseRepo unmarshalCourseRepo() throws Exception {       
    AegisReader<XMLStreamReader> reader = context.createXMLStreamReader();
    XMLStreamReader xmlReader = XMLInputFactory.newInstance()
      .createXMLStreamReader(new FileInputStream(fileName));
    
    CourseRepo courseRepo = (CourseRepo) reader.read(
      xmlReader, context.getTypeMapping().getType(CourseRepo.class));
    
    xmlReader.close();
    return courseRepo;
}

Следующий метод распаковывает документ XML в объект Java заданного типа:

Здесь объект AegisReader генерируется из экземпляра AegisContext. Затем объект AegisReader создает объект Java из предоставленного ввода. В этом примере входными данными является объект XMLStreamReader, поддерживаемый файлом, который мы создали в методе marshalCourseRepo, описанном выше.

5.4. Утверждения

@Test
public void whenMarshalingAndUnmarshalingCourseRepo_thenCorrect()
  throws Exception {
    initializeContext();
    CourseRepo inputRepo = initCourseRepo();
    marshalCourseRepo(inputRepo);
    CourseRepo outputRepo = unmarshalCourseRepo();
    Course restCourse = outputRepo.getCourses().get(1);
    Course securityCourse = outputRepo.getCourses().get(2);

    // JUnit assertions
}

Теперь пришло время объединить все вспомогательные методы, определенные в предыдущих подразделах, в тестовый метод:

assertEquals("Welcome to Beldung!", outputRepo.getGreeting());
assertEquals("REST with Spring", restCourse.getName());
assertEquals(new Date(1234567890000L), restCourse.getEnrolmentDate());
assertNull(restCourse.getInstructor());
assertEquals("Learn Spring Security", securityCourse.getName());
assertEquals(new Date(1456789000000L), securityCourse.getEnrolmentDate());
assertNull(securityCourse.getInstructor());

Сначала мы создаем экземпляр CourseRepo, затем маршалируем его в XML-документ и, наконец, демаршалируем документ для воссоздания. исходный объект. Давайте проверим, что воссозданный объект является тем, что мы ожидаем:

Понятно, что, кроме свойства инструктора, все остальные имеют восстановленные значения, включая свойство enrolmentDate со значениями типа Date. Это именно то, чего мы ожидаем, поскольку мы проинструктировали Aegis игнорировать свойство инструктора при маршалинге объектов курса.

5.5. Выходной XML-документ

<ns1:baeldung xmlns:ns1="http://aegis.cxf.baeldung.com"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:type="ns1:CourseRepo">
    <ns1:courses>
        <ns2:entry xmlns:ns2="urn:org.apache.cxf.aegis.types">
            <ns2:key>1</ns2:key>
            <ns2:value xsi:type="ns1:Course">
                <ns1:enrolmentDate>2009-02-14T06:31:30+07:00
                </ns1:enrolmentDate>
                <ns1:id>1</ns1:id>
                <ns1:instructor>Eugen</ns1:instructor>
                <ns1:name>REST with Spring</ns1:name>
            </ns2:value>
        </ns2:entry>
        <ns2:entry xmlns:ns2="urn:org.apache.cxf.aegis.types">
            <ns2:key>2</ns2:key>
            <ns2:value xsi:type="ns1:Course">
                <ns1:enrolmentDate>2016-03-01T06:36:40+07:00
                </ns1:enrolmentDate>
                <ns1:id>2</ns1:id>
                <ns1:instructor>Eugen</ns1:instructor>
                <ns1:name>Learn Spring Security</ns1:name>
            </ns2:value>
        </ns2:entry>
    </ns1:courses>
    <ns1:greeting>Welcome to Beldung!</ns1:greeting>
</ns1:baeldung>

Чтобы сделать эффект файлов сопоставления Aegis явным, мы показываем XML-документ без настройки ниже:

<ns1:baeldung xmlns:ns1="http://aegis.cxf.baeldung.com"
    xmlns:ns="http://courserepo.baeldung.com"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:type="ns:Baeldung" greeting="Welcome to Beldung!">
    <ns:courses>
        <ns2:entry xmlns:ns2="urn:org.apache.cxf.aegis.types">
            <ns2:key>1</ns2:key>
            <ns2:value xsi:type="ns1:Course">
                <ns1:enrolmentDate>2009-02-14T06:31:30+07:00
                </ns1:enrolmentDate>
                <ns1:id>1</ns1:id>
                <ns1:name>REST with Spring</ns1:name>
            </ns2:value>
        </ns2:entry>
        <ns2:entry xmlns:ns2="urn:org.apache.cxf.aegis.types">
            <ns2:key>2</ns2:key>
            <ns2:value xsi:type="ns1:Course">
                <ns1:enrolmentDate>2016-03-01T06:36:40+07:00
                </ns1:enrolmentDate>
                <ns1:id>2</ns1:id>
                <ns1:name>Learn Spring Security</ns1:name>
            </ns2:value>
        </ns2:entry>
    </ns:courses>
</ns1:baeldung>

Сравните это со случаем, когда пользовательское сопоставление Aegis действует:

Вы можете найти эту XML-структуру в файле baeldung.xml прямо в главном каталоге проекта после запуска теста, определенного в этом разделе.

Вы увидите, что атрибут type и пространство имен элемента XML, соответствующего объекту CourseRepo, изменяются в соответствии с тем, что мы установили в файле CourseRepo.aegis.xml. Свойство приветствия также преобразуется в атрибут, а свойство инструктора объектов курса исчезает, как и ожидалось.

Стоит отметить, что по умолчанию Aegis преобразует базовый тип Java в наиболее подходящий тип схемы, например. из объектов Date в элементы xsd:dateTime, как показано в этом руководстве. Однако мы можем изменить эту конкретную привязку, установив конфигурацию в соответствующем файле сопоставления.

Пожалуйста, перейдите на домашнюю страницу Aegis, если вы хотите получить больше информации.

6. Заключение

В этом руководстве показано использование привязки данных Apache CXF Aegis в качестве автономной подсистемы. Он демонстрирует, как можно использовать Aegis для преобразования объектов Java в элементы XML и наоборот.

В этом руководстве также рассматривается, как настроить поведение привязки данных.