Introduction to Apache CXF Aegis Data Binding – Apache CXF Aegis数据绑定介绍

最后修改: 2016年 11月 17日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

This tutorial gives an introduction to Aegis data binding, a subsystem that can map between Java objects and XML documents described by XML schemas. Aegis allows detailed control over the mapping process while keeping programming effort to a minimum.

本教程介绍了Aegis数据绑定,这是一个子系统,可以在Java对象和XML模式所描述的XML文档之间进行映射。Aegis允许对映射过程进行详细的控制,同时将编程工作降到最低。

Aegis is part of Apache CXF, but not constrained to be used within this framework only. Instead, this data binding mechanism may be used anywhere and hence in this tutorial we focus on its usage as an independent subsystem.

Aegis是Apache CXF的一部分,但并不局限于只在这个框架内使用。相反,这种数据绑定机制可以在任何地方使用,因此在本教程中,我们将重点介绍其作为一个独立子系统的使用。

2. Maven Dependencies

2.Maven的依赖性

The only dependency required to activate Aegis data binding is:

激活Aegis数据绑定所需的唯一依赖性是。

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

The latest version of this artifact can be found here.

该工件的最新版本可以在这里找到。

3. Type Definitions

3.类型定义

This section walks through the definitions of three types used to illustrate Aegis.

本节介绍了用于说明Aegis的三种类型的定义。

3.1. Course

3.1.课程

This is the simplest class in our example, which is defined as:

这是我们的例子中最简单的类,它的定义是:。

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

    // standard getters and setters
}

3.2. CourseRepo

3.2.CourseRepo

CourseRepo is the top-level type in our model. We define it as an interface rather than a class to demonstrate how easy it is to marshal a Java interface, which is impossible in JAXB without a custom adapter:

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

Note that we declare the getCourses method with the return type Map. This is intentional to express another advantage of Aegis over JAXB. The latter cannot marshal a map without a custom adapter while the former can.

请注意,我们声明getCourses方法的返回类型是Map。这是故意的,以表达Aegis比JAXB的另一个优势。后者在没有自定义适配器的情况下不能调集一个地图,而前者可以。

3.3. CourseRepoImpl

3.3.CourseRepoImpl

This class provides implementation to the CourseRepo interface:

该类提供对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. Custom Data Binding

4.自定义数据绑定

In order for the customization to take effect, XML mapping files must be present on the classpath. It is required that those files are placed into a directory whose structure corresponds to the package hierarchy of the associated Java types.

为了使定制生效,XML映射文件必须存在于classpath上。要求这些文件放在一个目录中,其结构与相关的Java类型的包层次结构相一致。

For example, if a fully qualified name class is named package.ClassName, its associated mapping file must be inside the package/ClassName subdirectory on the classpath. The name of a mapping file must be equal to the associated Java type with a .aegis.xml suffix appended to it.

例如,如果一个完全合格的名字类被命名为package.ClassName,它的相关映射文件必须在classpath上的package/ClassName子目录内。映射文件的名称必须等于相关的 Java 类型,并在其后面加上 .egis.xml 的后缀。

4.1. CourseRepo Mapping

4.1.CourseRepoMapping

The CourseRepo interface belongs to the com.baeldung.cxf.aegis package, so its corresponding mapping file is named CourseRepo.aegis.xml and put into the com/baeldung/cxf/aegis directory on the classpath.

CourseRepo接口属于com.baeldung.cxf.egis包,所以它相应的映射文件被命名为CourseRepo.egis.xml,并放入classpath的com/baeldung/cxf/aegis目录下。

In the CourseRepo mapping file, we change name and namespace of the XML element associated with the CourseRepo interface, as well as the style of its greeting property:

CourseRepo映射文件中,我们改变了与CourseRepo接口相关的XML元素的名称和命名空间,以及其greeting属性的样式。

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

4.2. Course Mapping

4.2.课程规划

Similar to the CourseRepo type, the mapping file of class Course is named Course.aegis.xml and located in the com/baeldung/cxf/aegis directory as well.

CourseRepo类型类似,Course类的映射文件被命名为Course.aegis.xml,也位于com/baeldung/cxf/aegis目录中。

In this mapping file, we instruct Aegis to ignore the instructor property of the Course class when marshaling, so that its value is not available in the object recreated from the output XML document:

在这个映射文件中,我们指示Aegis在处理时忽略Course类的instructor属性,这样,在从输出的XML文档中重新创建的对象中就没有它的值。

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

Aegis’ home page is where we can find more customization options.

Aegis的主页是我们可以找到更多定制选项的地方。

5. Testing

5.测试

This section is a step-by-step guide to set up and execute a test case that illustrates the usage of Aegis data bindings.

本节是一个逐步的指南,用来设置和执行一个测试案例,说明Aegis数据绑定的用法。

To facilitate the testing process, we declare two fields within the test class:

为了方便测试过程,我们在测试类中声明两个字段。

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

    // other methods
}

These fields have been defined here to be used by other methods of this class.

这些字段被定义在这里,以便被这个类的其他方法使用。

5.1. AegisContext Initialization

5.1.AegisContext初始化

First, an AegisContext object must be created:

首先,必须创建一个AegisContext对象。

context = new AegisContext();

That AegisContext instance is then configured and initialized. Here is how we set root classes for the context:

然后对AegisContext实例进行配置和初始化。下面是我们如何为上下文设置根类。

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

Aegis creates an XML mapping element for each Type within the Set<Type> object. In this tutorial, we set only CourseRepo as a root type.

Aegis为Set<Type>对象中的每个Type创建一个XML映射元素。在本教程中,我们只设置CourseRepo作为根类型。

Now, let’s set the implementation map for the context to specify the proxy class for the CourseRepo interface:

现在,让我们为上下文设置实现图,指定CourseRepo接口的代理类。

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

The last configuration for the Aegis context is telling it to set the xsi:type attribute in the corresponding XML document. This attribute carries the actual type name of the associated Java object unless overridden by the mapping file:

Aegis上下文的最后一项配置是告诉它在相应的XML文档中设置xsi:type属性。这个属性带有相关Java对象的实际类型名称,除非被映射文件覆盖。

context.setWriteXsiTypes(true);

Our AegisContext instance is now ready to be initialized:

我们的AegisContext实例现在可以被初始化了。

context.initialize();

To keep the code clean, we collect all code snippets from this subsection into one helper method:

为了保持代码的简洁,我们将本小节的所有代码片段收集到一个辅助方法中。

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

5.2. Simple Data Setup

5.2.简单的数据设置

Due to the simple nature of this tutorial, we generate sample data right in memory rather than relying on a persistent solution. Let’s populate the course repo using the setup logic below:

由于本教程的简单性质,我们在内存中直接生成样本数据,而不是依赖一个持久的解决方案。让我们使用下面的设置逻辑来填充课程回放。

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. Binding Java Objects and XML Elements

5.3.绑定Java对象和XML元素

The steps that need to be taken to marshal Java objects to XML elements are illustrated with the following helper method:

用下面的辅助方法来说明将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();
}

As we can see, the AegisWriter and AegisType objects must be created from the AegisContext instance. The AegisWriter object then marshals the given Java instance to the specified output.

我们可以看到,AegisWriterAegisType对象必须由AegisContext实例创建。然后,AegisWriter对象将给定的Java实例编入指定的输出。

In this case, this is an XMLStreamWriter object associated with a file named after the value of the fileName class-level field in the file system.

在这种情况下,这是一个XMLStreamWriter对象,与文件系统中以fileName类的值命名的文件有关。

The following method unmarshals an XML document to a Java object of the given type:

下面的方法将一个XML文档解读为一个给定类型的Java对象。

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

Here, an AegisReader object is generated from the AegisContext instance. The AegisReader object then creates a Java object out of the provided input. In this example, that input is an XMLStreamReader object backed by the file we generated in the marshalCourseRepo method described right above.

这里,一个AegisReader对象从AegisContext实例中生成。然后,AegisReader对象从提供的输入中创建一个Java对象。在这个例子中,这个输入是一个XMLStreamReader对象,它由我们在上面描述的marshalCourseRepo方法中生成的文件支持。

5.4. Assertions

5.4.断言

Now, it is time to combine all helper methods defined in previous subsections into a test method:

现在,是时候把前面几个小节中定义的所有辅助方法合并成一个测试方法了。

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

We first create a CourseRepo instance, then marshal it to an XML document and finally unmarshal the document to recreate the original object. Let’s verify that the recreated object is what we expect:

我们首先创建一个CourseRepo实例,然后将其编入一个XML文档,最后取消编入文档以重新创建原始对象。让我们来验证一下,重新创建的对象是我们所期望的。

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

It is clear that except for the instructor property, all other ones have their values recovered, including the enrolmentDate property with values of type Date. This is exactly what we expect as we have instructed Aegis to ignore the instructor property when marshaling Course objects.

很明显,除了instructor属性,所有其他的属性都恢复了它们的值,包括enrolmentDate属性,其值是Date类型。这正是我们所期望的,因为我们已经指示Aegis在处理Course对象时忽略instructor属性。

5.5. Output XML Document

5.5.输出XML文件

To make the effect of Aegis mapping files explicit, we show the XML document without customization below:

为了明确Aegis映射文件的效果,我们在下面展示没有定制的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>

Compare this with the case when Aegis custom mapping is in action:

将这一点与Aegis自定义映射发挥作用时的情况进行比较。

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

You may find this XML structure in the baeldung.xml, right inside the project’s main directory after running the test defined in this section.

你可以在运行本节定义的测试后,在项目主目录下的baeldung.xml中找到这个XML结构。

You will see that the type attribute and namespace of the XML element corresponding to the CourseRepo object change in accordance with what we set in the CourseRepo.aegis.xml file. The greeting property is also transformed into an attribute, and the instructor property of Course objects disappears as expected.

你会看到对应于CourseRepo对象的XML元素的type属性和命名空间按照我们在CourseRepo.aegis.xml文件中的设置而改变。问候属性也被转化为一个属性,课程对象的讲师属性也如预期的那样消失了。

It is worth to note that by default Aegis converts a basic Java type to the best matching schema type, e.g. from Date objects to xsd:dateTime elements as shown in this tutorial. However, we can change that particular binding by setting the configuration in the corresponding mapping file.

值得注意的是,默认情况下,Aegis把一个基本的Java类型转换成最匹配的模式类型,例如,从Date对象转换成xsd:dateTime元素,如本教程所示。然而,我们可以通过在相应的映射文件中设置配置来改变这种特定的绑定。

Please navigate to the Aegis home page if you want to have more information.

如果你想获得更多信息,请浏览Aegis主页

6. Conclusion

6.结论

This tutorial illustrates the use of the Apache CXF Aegis data binding as a standalone subsystem. It demonstrates how Aegis may be used to map Java objects to XML elements, and vice versa.

本教程说明了Apache CXF Aegis数据绑定作为一个独立的子系统的使用。它演示了如何用Aegis将Java对象映射到XML元素,反之亦然。

The tutorial also focuses on how to customize data binding behaviors.

该教程还重点介绍了如何定制数据绑定行为。

And, as always, the implementation of all these examples and code snippets can be found in the GitHub project.

而且,像往常一样,所有这些例子和代码片断的实现都可以在GitHub项目中找到。