A Guide to Mapping With Dozer – 使用推土机测绘的指南

最后修改: 2016年 8月 4日

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

1. Overview

1.概述

Dozer is a Java Bean to Java Bean mapper that recursively copies data from one object to another, attribute by attribute.

Dozer是一个Java Bean到Java Bean的映射器,它可以将数据从一个对象递归地复制到另一个对象的属性。

The library not only supports mapping between attribute names of Java Beans, but also automatically converts between types – if they’re different.

该库不仅支持Java Beans的属性名称之间的映射,而且还支持类型之间的自动转换–如果它们是不同的。

Most conversion scenarios are supported out of the box, but Dozer also allows you to specify custom conversions via XML.

大多数转换方案都支持开箱即用,但Dozer也允许你通过XML指定自定义转换

2. Simple Example

2.简单的例子

For our first example, let’s assume that the source and destination data objects all share the same common attribute names.

对于我们的第一个例子,让我们假设源和目的数据对象都有相同的共同属性名称。

This is the most basic mapping one can do with Dozer:

这是人们可以用Dozer做的最基本的制图。

public class Source {
    private String name;
    private int age;

    public Source() {}

    public Source(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    // standard getters and setters
}

Then our destination file, Dest.java:

然后是我们的目标文件,Dest.java

public class Dest {
    private String name;
    private int age;

    public Dest() {}

    public Dest(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    // standard getters and setters
}

We need to make sure to include the default or zero argument constructors, since Dozer uses reflection under the hood.

我们需要确保包括默认或零参数构造函数,因为Dozer在引擎盖下使用了反射。

And, for performance purposes, let’s make our mapper global and create a single object we’ll use throughout our tests:

而且,为了性能的目的,让我们使我们的映射器全局化,并创建一个单一的对象,我们将在整个测试中使用。

DozerBeanMapper mapper;

@Before
public void before() throws Exception {
    mapper = new DozerBeanMapper();
}

Now, let’s run our first test to confirm that when we create a Source object, we can map it directly onto a Dest object:

现在,让我们运行第一个测试,以确认当我们创建一个Source对象时,我们可以将其直接映射到Dest对象。

@Test
public void givenSourceObjectAndDestClass_whenMapsSameNameFieldsCorrectly_
  thenCorrect() {
    Source source = new Source("Baeldung", 10);
    Dest dest = mapper.map(source, Dest.class);

    assertEquals(dest.getName(), "Baeldung");
    assertEquals(dest.getAge(), 10);
}

As we can see, after the Dozer mapping, the result will be a new instance of the Dest object that contains values for all fields that have the same field name as the Source object.

正如我们所看到的,在Dozer映射之后,结果将是一个新的Dest 对象的实例,其中包含与Source对象有相同字段名的所有字段的值。

Alternatively, instead of passing mapper the Dest class, we could just have created the Dest object and passed mapper its reference:

另外,我们可以不向mapper传递Dest类,而是直接创建Dest对象并向mapper传递其引用。

@Test
public void givenSourceObjectAndDestObject_whenMapsSameNameFieldsCorrectly_
  thenCorrect() {
    Source source = new Source("Baeldung", 10);
    Dest dest = new Dest();
    mapper.map(source, dest);

    assertEquals(dest.getName(), "Baeldung");
    assertEquals(dest.getAge(), 10);
}

3. Maven Setup

3.Maven设置

Now that we have a basic understanding of how Dozer works, let’s add the following dependency to the pom.xml:

现在我们对Dozer的工作原理有了基本的了解,让我们在pom.xml中添加以下依赖关系。

<dependency>
    <groupId>net.sf.dozer</groupId>
    <artifactId>dozer</artifactId>
    <version>5.5.1</version>
</dependency>

The latest version is available here.

最新版本可在这里获得。

4. Data Conversion Example

4.数据转换实例

As we already know, Dozer can map an existing object to another as long as it finds attributes of the same name in both classes.

我们已经知道,只要在两个类中找到同名的属性,Dozer就可以将一个现有的对象映射到另一个对象。

However, that’s not always the case; and so, if any of the mapped attributes are of different data types, the Dozer mapping engine will automatically perform a data type conversion.

然而,情况并非总是如此;因此,如果任何映射的属性是不同的数据类型,Dozer映射引擎将自动执行数据类型转换

Let’s see this new concept in action:

让我们看看这个新概念的行动。

public class Source2 {
    private String id;
    private double points;

    public Source2() {}

    public Source2(String id, double points) {
        this.id = id;
        this.points = points;
    }
    
    // standard getters and setters
}

And the destination class:

还有目的地类。

public class Dest2 {
    private int id;
    private int points;

    public Dest2() {}

    public Dest2(int id, int points) {
        super();
        this.id = id;
        this.points = points;
    }
    
    // standard getters and setters
}

Notice that the attribute names are the same but their data types are different.

注意,属性名称是相同的,但它们的数据类型是不同的

In the source class, id is a String and points is a double, whereas in the destination class, id and points are both integers.

在源类中,id是一个Stringpoints是一个double,而在目的类中,idpoints都是integers。

Let’s now see how Dozer correctly handles the conversion:

现在让我们看看Dozer是如何正确处理转换的。

@Test
public void givenSourceAndDestWithDifferentFieldTypes_
  whenMapsAndAutoConverts_thenCorrect() {
    Source2 source = new Source2("320", 15.2);
    Dest2 dest = mapper.map(source, Dest2.class);

    assertEquals(dest.getId(), 320);
    assertEquals(dest.getPoints(), 15);
}

We passed “320” and 15.2, a String and a double into the source object and the result had 320 and 15, both integers in the destination object.

我们把“320”15.2,一个字符串和一个双数传入源对象,结果在目标对象中有32015,两个整数s。

5. Basic Custom Mappings via XML

5.通过XML的基本自定义映射

In all the previous examples we have seen, both the source and destination data objects have the same field names, which allows for easy mapping on our side.

在我们之前看到的所有例子中,源数据对象和目标数据对象都有相同的字段名,这使得我们这边的映射很容易。

However, in real world applications, there will be countless times where the two data objects we’re mapping won’t have fields that share a common property name.

然而,在现实世界的应用中,会有无数次我们要映射的两个数据对象没有共享一个共同属性名称的字段。

To solve this, Dozer gives us an option to create a custom mapping configuration in XML.

为了解决这个问题,Dozer给了我们一个选项,可以在XML中创建一个自定义映射配置

In this XML file, we can define class mapping entries which the Dozer mapping engine will use to decide what source attribute to map to what destination attribute.

在这个XML文件中,我们可以定义类映射条目,Dozer映射引擎将使用这些条目来决定将什么源属性映射到什么目标属性。

Let’s have a look at an example, and let’s try unmarshalling data objects from an application built by a French programmer, to an English style of naming our objects.

让我们来看一个例子,让我们尝试从一个法国程序员建立的应用程序中解读数据对象,以英语风格来命名我们的对象。

We have a Person object with name, nickname and age fields:

我们有一个Person对象,有namenicknameage字段。

public class Person {
    private String name;
    private String nickname;
    private int age;

    public Person() {}

    public Person(String name, String nickname, int age) {
        super();
        this.name = name;
        this.nickname = nickname;
        this.age = age;
    }
    
    // standard getters and setters
}

The object we are unmarshalling is named Personne and has fields nom, surnom and age:

我们要解冻的对象名为Personne,并且有nomsurnomage字段。

public class Personne {
    private String nom;
    private String surnom;
    private int age;

    public Personne() {}

    public Personne(String nom, String surnom, int age) {
        super();
        this.nom = nom;
        this.surnom = surnom;
        this.age = age;
    }
    
    // standard getters and setters
}

These objects really achieve the same purpose but we have a language barrier. In order to help with that barrier, we can use Dozer to map the French Personne object to our Person object.

这些对象确实达到了相同的目的,但是我们有语言障碍。为了帮助解决这个障碍,我们可以使用Dozer将法国的Personne对象映射到我们的Person对象。

We only have to create a custom mapping file to help Dozer do this, we will call it dozer_mapping.xml:

我们只需要创建一个自定义的映射文件来帮助Dozer做这件事,我们将其称为dozer_mapping.xml

<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://dozer.sourceforge.net
      http://dozer.sourceforge.net/schema/beanmapping.xsd">
    <mapping>
        <class-a>com.baeldung.dozer.Personne</class-a>
        <class-b>com.baeldung.dozer.Person</class-b>
        <field>
            <a>nom</a>
            <b>name</b>
        </field>
        <field>
            <a>surnom</a>
            <b>nickname</b>
        </field>
    </mapping>
</mappings>

This is the simplest example of a custom XML mapping file we can have.

这是我们可以拥有的最简单的自定义XML映射文件的例子。

For now, it’s enough to notice that we have <mappings> as our root element, which has a child <mapping>, we can have as many of these children inside <mappings> as there are incidences of class pairs that need custom mapping.

现在,只要注意到我们有<mappings>作为我们的根元素,它有一个子元素<mapping>就足够了,我们可以在<mappings>里面有多少个这样的子元素,因为有需要自定义映射的类对发生了。

Notice also how we specify the source and destination classes inside the <mapping></mapping> tags. This is followed by a <field></field> for each source and destination field pair that need custom mapping.

请注意我们是如何在<mapping></mapping>标签内指定源和目标类的。这后面是需要自定义映射的每个源和目的字段对的<field></field>

Finally, notice that we have not included the field age in our custom mapping file. The French word for age is still age, which brings us to another important feature of Dozer.

最后,注意到我们在自定义映射文件中没有包括age这个字段。年龄的法语单词仍然是age,这给我们带来了Dozer的另一个重要特征。

Properties that are of the same name do not need to be specified in the mapping XML file. Dozer automatically maps all fields with the same property name from the source object into the destination object.

同名的属性不需要在映射的XML文件中指定。Dozer会自动将源对象中具有相同属性名称的所有字段映射到目标对象中。

We will then place our custom XML file on the classpath directly under the src folder. However, wherever we place it on the classpath, Dozer will search the entire classpath looking for the specified file.

然后我们将把我们的自定义XML文件放在classpath上,直接放在src文件夹下。然而,无论我们把它放在classpath的什么地方,Dozer都会搜索整个classpath,寻找指定的文件。

Let us create a helper method to add mapping files to our mapper:

让我们创建一个辅助方法,向我们的mapper添加映射文件。

public void configureMapper(String... mappingFileUrls) {
    mapper.setMappingFiles(Arrays.asList(mappingFileUrls));
}

Let’s now test the code:

现在我们来测试一下这段代码。

@Test
public void givenSrcAndDestWithDifferentFieldNamesWithCustomMapper_
  whenMaps_thenCorrect() {
    configureMapper("dozer_mapping.xml");
    Personne frenchAppPerson = new Personne("Sylvester Stallone", "Rambo", 70);
    Person englishAppPerson = mapper.map(frenchAppPerson, Person.class);

    assertEquals(englishAppPerson.getName(), frenchAppPerson.getNom());
    assertEquals(englishAppPerson.getNickname(), frenchAppPerson.getSurnom());
    assertEquals(englishAppPerson.getAge(), frenchAppPerson.getAge());
}

As shown in the test, DozerBeanMapper accepts a list of custom XML mapping files and decides when to use each at runtime.

如测试所示,DozerBeanMapper接受了一个自定义XML映射文件的列表,并在运行时决定何时使用每个文件。

Assuming we now start unmarshalling these data objects back and forth between our English app and the French app. We don’t need to create another mapping in the XML file, Dozer is smart enough to map the objects both ways with only one mapping configuration:

假设我们现在开始在我们的英语应用和法语应用之间来回解读这些数据对象。我们不需要在XML文件中创建另一个映射,Dozer足够聪明,只需一个映射配置就能将对象双向映射

@Test
public void givenSrcAndDestWithDifferentFieldNamesWithCustomMapper_
  whenMapsBidirectionally_thenCorrect() {
    configureMapper("dozer_mapping.xml");
    Person englishAppPerson = new Person("Dwayne Johnson", "The Rock", 44);
    Personne frenchAppPerson = mapper.map(englishAppPerson, Personne.class);

    assertEquals(frenchAppPerson.getNom(), englishAppPerson.getName());
    assertEquals(frenchAppPerson.getSurnom(),englishAppPerson.getNickname());
    assertEquals(frenchAppPerson.getAge(), englishAppPerson.getAge());
}

And so this example test uses this another feature of Dozer – the fact that the Dozer mapping engine is bi-directional, so if we want to map the destination object to the source object, we do not need to add another class mapping to the XML file.

因此,这个例子测试使用了Dozer的另一个特点–Dozer映射引擎是双向的,所以如果我们想把目标对象映射到源对象,我们不需要在XML文件中添加另一个类映射。

We can also load a custom mapping file from outside the classpath, if we need to, use the “file:” prefix in the resource name.

我们也可以从classpath之外加载一个自定义映射文件,如果需要的话,在资源名称中使用”file:“前缀。

On a Windows environment (such as the test below), we’ll of course use the Windows specific file syntax.

在Windows环境下(比如下面的测试),我们当然会使用Windows特有的文件语法。

On a Linux box, we may store the file under /home and then:

在Linux盒子上,我们可以将文件存储在/home下,然后。

configureMapper("file:/home/dozer_mapping.xml");

And on Mac OS:

而在Mac OS上。

configureMapper("file:/Users/me/dozer_mapping.xml");

If you are running the unit tests from the github project (which you should), you can copy the mapping file to the appropriate location and change the input for configureMapper method.

如果您从github项目中运行单元测试(您应该这样做),您可以将映射文件复制到适当的位置并更改configureMapper方法的输入。

The mapping file is available under test/resources folder of the GitHub project:

映射文件可在GitHub项目的test/resources文件夹下找到。

@Test
public void givenMappingFileOutsideClasspath_whenMaps_thenCorrect() {
    configureMapper("file:E:\\dozer_mapping.xml");
    Person englishAppPerson = new Person("Marshall Bruce Mathers III","Eminem", 43);
    Personne frenchAppPerson = mapper.map(englishAppPerson, Personne.class);

    assertEquals(frenchAppPerson.getNom(), englishAppPerson.getName());
    assertEquals(frenchAppPerson.getSurnom(),englishAppPerson.getNickname());
    assertEquals(frenchAppPerson.getAge(), englishAppPerson.getAge());
}

6. Wildcards and Further XML Customization

6.通配符和进一步的XML定制

Let’s create a second custom mapping file called dozer_mapping2.xml:

让我们创建第二个自定义映射文件,称为dozer_mapping2.xml

<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://dozer.sourceforge.net 
      http://dozer.sourceforge.net/schema/beanmapping.xsd">
    <mapping wildcard="false">
        <class-a>com.baeldung.dozer.Personne</class-a>
        <class-b>com.baeldung.dozer.Person</class-b>
        <field>
            <a>nom</a>
            <b>name</b>
        </field>
        <field>
            <a>surnom</a>
            <b>nickname</b>
        </field>
    </mapping>
</mappings>

Notice that we have added an attribute wildcard to the <mapping></mapping> element which was not there before.

请注意,我们在<mapping></mapping>元素中添加了一个属性wildcard,这个元素之前并不存在。

By default, wildcard is true. It tells the Dozer engine that we want all fields in the source object to be mapped to their appropriate destination fields.

默认情况下,wildcardtrue。它告诉Dozer引擎,我们希望源对象中的所有字段都被映射到它们适当的目标字段。

When we set it to false, we are telling Dozer to only map fields we have explicitly specified in the XML.

当我们把它设置为false时,我们告诉Dozer只映射我们在XML中明确指定的字段。

So in the above configuration, we only want two fields mapped, leaving out age:

因此,在上述配置中,我们只想让两个字段被映射,而不考虑年龄

@Test
public void givenSrcAndDest_whenMapsOnlySpecifiedFields_thenCorrect() {
    configureMapper("dozer_mapping2.xml");
    Person englishAppPerson = new Person("Shawn Corey Carter","Jay Z", 46);
    Personne frenchAppPerson = mapper.map(englishAppPerson, Personne.class);

    assertEquals(frenchAppPerson.getNom(), englishAppPerson.getName());
    assertEquals(frenchAppPerson.getSurnom(),englishAppPerson.getNickname());
    assertEquals(frenchAppPerson.getAge(), 0);
}

As we can see in the last assertion, the destination age field remained 0.

正如我们在最后一个断言中所看到的,目的地年龄字段仍然是0

7. Custom Mapping via Annotations

7.通过注解的自定义映射

For simple mapping cases and cases where we also have write access to the data objects we would like to map, we may not need to use XML mapping.

对于简单的映射情况,以及我们也有对我们想要映射的数据对象的写入权限的情况,我们可能不需要使用XML映射。

Mapping differently named fields via annotations is very simple and we have to write much less code than in XML mapping but can only help us in simple cases.

通过注解来映射不同命名的字段是非常简单的,我们要写的代码比XML映射少得多,但只能在简单情况下帮助我们。

Let’s replicate our data objects into Person2.java and Personne2.java without changing the fields at all.

让我们把我们的数据对象复制到Person2.javaPersonne2.java,完全不改变字段。

To implement this, we only need to add @mapper(“destinationFieldName”) annotation on the getter methods in the source object. Like so:

为了实现这一点,我们只需要在源对象的getter方法上添加@mapper(“destinationFieldName”)注解。就像这样。

@Mapping("name")
public String getNom() {
    return nom;
}

@Mapping("nickname")
public String getSurnom() {
    return surnom;
}

This time we are treating Personne2 as the source, but it does not matter due to the bi-directional nature of the Dozer Engine.

这次我们把Personne2作为源头,但由于推土机引擎的双向性质,这并不重要。

Now with all the XML related code stripped out, our test code is shorter:

现在所有与XML相关的代码都被剥离出来,我们的测试代码就更短了。

@Test
public void givenAnnotatedSrcFields_whenMapsToRightDestField_thenCorrect() {
    Person2 englishAppPerson = new Person2("Jean-Claude Van Damme", "JCVD", 55);
    Personne2 frenchAppPerson = mapper.map(englishAppPerson, Personne2.class);

    assertEquals(frenchAppPerson.getNom(), englishAppPerson.getName());
    assertEquals(frenchAppPerson.getSurnom(), englishAppPerson.getNickname());
    assertEquals(frenchAppPerson.getAge(), englishAppPerson.getAge());
}

We can also test for bi-directionality:

我们还可以测试双向性。

@Test
public void givenAnnotatedSrcFields_whenMapsToRightDestFieldBidirectionally_
  thenCorrect() {
    Personne2 frenchAppPerson = new Personne2("Jason Statham", "transporter", 49);
    Person2 englishAppPerson = mapper.map(frenchAppPerson, Person2.class);

    assertEquals(englishAppPerson.getName(), frenchAppPerson.getNom());
    assertEquals(englishAppPerson.getNickname(), frenchAppPerson.getSurnom());
    assertEquals(englishAppPerson.getAge(), frenchAppPerson.getAge());
}

8. Custom API Mapping

8.自定义API映射

In our previous examples where we are unmarshalling data objects from a french application, we used XML and annotations to customize our mapping.

在我们之前的例子中,我们从一个法国的应用程序中解读数据对象,我们使用XML和注解来定制我们的映射。

Another alternative available in Dozer, similar to annotation mapping is API mapping. They are similar because we eliminate XML configuration and strictly use Java code.

Dozer中另一个可供选择的,与注解映射类似的是API映射。它们之所以相似,是因为我们取消了XML配置,严格使用Java代码。

In this case, we use BeanMappingBuilder class, defined in our simplest case like so:

在这种情况下,我们使用BeanMappingBuilder类,在我们最简单的情况下这样定义。

BeanMappingBuilder builder = new BeanMappingBuilder() {
    @Override
    protected void configure() {
        mapping(Person.class, Personne.class)
          .fields("name", "nom")
            .fields("nickname", "surnom");
    }
};

As we can see, we have an abstract method, configure(), which we must override to define our configurations. Then, just like our <mapping></mapping> tags in XML, we define as many TypeMappingBuilders as we require.

正如我们所看到的,我们有一个抽象的方法,configure(),我们必须覆盖它来定义我们的配置。然后,就像我们在 XML 中的 <mapping></mapping> 标签一样,我们根据需要定义尽可能多的 TypeMappingBuilders。

These builders tell Dozer which source to destination fields we are mapping. We then pass the BeanMappingBuilder to DozerBeanMapper as we would, the XML mapping file, only with a different API:

这些构建器告诉Dozer我们要映射哪些源字段到目标字段。然后我们将BeanMappingBuilder传递给DozerBeanMapper,就像我们传递XML映射文件一样,只是使用不同的API。

@Test
public void givenApiMapper_whenMaps_thenCorrect() {
    mapper.addMapping(builder);
 
    Personne frenchAppPerson = new Personne("Sylvester Stallone", "Rambo", 70);
    Person englishAppPerson = mapper.map(frenchAppPerson, Person.class);

    assertEquals(englishAppPerson.getName(), frenchAppPerson.getNom());
    assertEquals(englishAppPerson.getNickname(), frenchAppPerson.getSurnom());
    assertEquals(englishAppPerson.getAge(), frenchAppPerson.getAge());
}

The mapping API is also bi-directional:

映射API也是双向的。

@Test
public void givenApiMapper_whenMapsBidirectionally_thenCorrect() {
    mapper.addMapping(builder);
 
    Person englishAppPerson = new Person("Sylvester Stallone", "Rambo", 70);
    Personne frenchAppPerson = mapper.map(englishAppPerson, Personne.class);

    assertEquals(frenchAppPerson.getNom(), englishAppPerson.getName());
    assertEquals(frenchAppPerson.getSurnom(), englishAppPerson.getNickname());
    assertEquals(frenchAppPerson.getAge(), englishAppPerson.getAge());
}

Or we can choose to only map explicitly specified fields with this builder configuration:

或者我们可以选择只用这个构建器配置来映射明确指定的字段。

BeanMappingBuilder builderMinusAge = new BeanMappingBuilder() {
    @Override
    protected void configure() {
        mapping(Person.class, Personne.class)
          .fields("name", "nom")
            .fields("nickname", "surnom")
              .exclude("age");
    }
};

and our age==0 test is back:

而我们的年龄==0测试回来了。

@Test
public void givenApiMapper_whenMapsOnlySpecifiedFields_thenCorrect() {
    mapper.addMapping(builderMinusAge); 
    Person englishAppPerson = new Person("Sylvester Stallone", "Rambo", 70);
    Personne frenchAppPerson = mapper.map(englishAppPerson, Personne.class);

    assertEquals(frenchAppPerson.getNom(), englishAppPerson.getName());
    assertEquals(frenchAppPerson.getSurnom(), englishAppPerson.getNickname());
    assertEquals(frenchAppPerson.getAge(), 0);
}

9. Custom Converters

9.自定义转换器

Another scenario we may face in mapping is where we would like to perform custom mapping between two objects.

我们在映射中可能面临的另一种情况是,我们想在两个对象之间执行自定义映射

We have looked at scenarios where source and destination field names are different like in the French Personne object. This section solves a different problem.

我们已经研究了源字段和目的字段名称不同的情况,比如在法国Personne对象中。本节解决了一个不同的问题。

What if a data object we are unmarshalling represents a date and time field such as a long or Unix time like so:

如果我们正在解读的数据对象代表了一个日期和时间字段,如long或Unix时间,像这样,怎么办?

1182882159000

But our own equivalent data object represents the same date and time field and value in this ISO format such as a String:

但我们自己的等效数据对象以这种ISO格式表示相同的日期和时间字段和值,如String:

2007-06-26T21:22:39Z

The default converter would simply map the long value to a String like so:

默认的转换器会简单地将长值映射为String,像这样。

"1182882159000"

This would definitely bug our app. So how do we solve this? We solve it by adding a configuration block in the mapping XML file and specifying our own converter.

这肯定会给我们的应用程序带来麻烦。那么我们如何解决这个问题呢?我们通过在映射XML文件中添加一个配置块指定我们自己的转换器来解决这个问题。

First, let’s replicate the remote application’s Person DTO with a name, then date and time of birth, dtob field:

首先,让我们复制远程应用程序的Person DTO,有一个name,然后是date and time of birth, dtob字段。

public class Personne3 {
    private String name;
    private long dtob;

    public Personne3(String name, long dtob) {
        super();
        this.name = name;
        this.dtob = dtob;
    }
    
    // standard getters and setters
}

and here is our own:

而这里是我们自己的。

public class Person3 {
    private String name;
    private String dtob;

    public Person3(String name, String dtob) {
        super();
        this.name = name;
        this.dtob = dtob;
    }
    
    // standard getters and setters
}

Notice the type difference of dtob in the source and destination DTOs.

请注意源和目的DTO中dtob的类型差异。

Let’s also create our own CustomConverter to pass to Dozer in the mapping XML:

让我们也创建我们自己的CustomConverter,在映射XML中传递给Dozer。

public class MyCustomConvertor implements CustomConverter {
    @Override
    public Object convert(Object dest, Object source, Class<?> arg2, Class<?> arg3) {
        if (source == null) 
            return null;
        
        if (source instanceof Personne3) {
            Personne3 person = (Personne3) source;
            Date date = new Date(person.getDtob());
            DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
            String isoDate = format.format(date);
            return new Person3(person.getName(), isoDate);

        } else if (source instanceof Person3) {
            Person3 person = (Person3) source;
            DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
            Date date = format.parse(person.getDtob());
            long timestamp = date.getTime();
            return new Personne3(person.getName(), timestamp);
        }
    }
}

We only have to override convert() method then return whatever we want to return to it. We are availed with the source and destination objects and their class types.

我们只需要覆盖convert()方法,然后返回我们想返回的东西。我们得到了源对象和目标对象以及它们的类别类型。

Notice how we have taken care of bi-directionality by assuming the source can be either of the two classes we are mapping.

注意我们是如何通过假设源可以是我们所映射的两个类中的任何一个来照顾到双向性的。

We will create a new mapping file for clarity, dozer_custom_convertor.xml:

为了清楚起见,我们将创建一个新的映射文件,dozer_custom_convertor.xml

<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://dozer.sourceforge.net
      http://dozer.sourceforge.net/schema/beanmapping.xsd">
    <configuration>
        <custom-converters>
            <converter type="com.baeldung.dozer.MyCustomConvertor">
                <class-a>com.baeldung.dozer.Personne3</class-a>
                <class-b>com.baeldung.dozer.Person3</class-b>
            </converter>
        </custom-converters>
    </configuration>
</mappings>

This is the normal mapping file we have seen in preceding sections, we have only added a <configuration></configuration> block within which we can define as many custom converters as we require with their respective source and destination data classes.

这是我们在前面几节中看到的正常的映射文件,我们只是添加了一个<configuration></configuration>块,在这个块中我们可以根据我们的需要定义尽可能多的自定义转换器以及各自的源和目标数据类。

Let’s test our new CustomConverter code:

让我们测试一下我们新的CustomConverter代码。

@Test
public void givenSrcAndDestWithDifferentFieldTypes_whenAbleToCustomConvert_
  thenCorrect() {

    configureMapper("dozer_custom_convertor.xml");
    String dateTime = "2007-06-26T21:22:39Z";
    long timestamp = new Long("1182882159000");
    Person3 person = new Person3("Rich", dateTime);
    Personne3 person0 = mapper.map(person, Personne3.class);

    assertEquals(timestamp, person0.getDtob());
}

We can also test to ensure it is bi-directional:

我们还可以测试以确保它是双向的。

@Test
public void givenSrcAndDestWithDifferentFieldTypes_
  whenAbleToCustomConvertBidirectionally_thenCorrect() {
    configureMapper("dozer_custom_convertor.xml");
    String dateTime = "2007-06-26T21:22:39Z";
    long timestamp = new Long("1182882159000");
    Personne3 person = new Personne3("Rich", timestamp);
    Person3 person0 = mapper.map(person, Person3.class);

    assertEquals(dateTime, person0.getDtob());
}

10. Conclusion

10.结论

In this tutorial, we have introduced most of the basics of the Dozer Mapping library and how to use it in our applications.

在本教程中,我们已经介绍了Dozer Mapping库的大部分基础知识以及如何在我们的应用程序中使用它。

The full implementation of all these examples and code snippets can be found in the Dozer github project.

所有这些例子和代码片段的完整实现可以在Dozer github项目中找到。