Orika uses a declarative Java-based configuration of mappings from one class to another, whereby you define which fields from one type need to be matched up with which fields from another using a fluent-style API.
For many cases, Orika can properly generate a suitable mapper using just the names of the fields you provide—by investigating their respective types using reflection. If the names of the fields you need to map match, you don’t even need to explicitly declare them—the byDefault() method of ClassMapBuilder will match these for you.
A ClassMapBuilder is obtained by calling the classMap(aType, bType) method on a MapperFactory instance. It’s best to start with an example — suppose we have some instances of class BasicPerson that we’d like to map to instances of another class BasicPersonDto, defined like so:
class BasicPerson { private String name; private int age; private Date birthDate; // getters/setters omitted } class BasicPersonDto { private String fullName; private int currentAge; private Date birthDate; // getters/setters omitted }
In order to map between these classes, we’ll need to first register a ClassMap for the two types, which is created like so:
mapperFactory.classMap(BasicPerson.class, BasicPersonDto.class) .field("name", "fullName") .field("age", "currentAge") .register();
In the case where one or more fields in the two types being mapped have matching names, the byDefault method can be used, like so:
mapperFactory.classMap(BasicPerson.class, BasicPersonDto.class) .field("name", "fullName") .field("age", "currentAge") .byDefault() .register();
By default, a field mapping added using the field(nameA, nameB) method is bi-directional. To declare a field mapping that is only used in a single direction, use one of the fieldAToB or fieldBToA methods, like so:
mapperFactory.classMap(BasicPerson.class, BasicPersonDto.class) .fieldAToB("name", "fullName") ... .register();
This would result in the ‘name’ field of BasicPerson being copied to the ‘fullName’ field of BasicPersonDto, but does not include mapping ‘fullName’ to ‘name’ when mapping is in the other direction.
To explicitly exclude a field from mapping, use the exclude(name), like so:
mapperFactory.classMap(BasicPerson.class, BasicPersonDto.class) .exclude("name") ... .register();
This would exclude the ‘name’ field from mapping.
Orika will attempt to find a best-fit match when choosing which constructor to use for instantiating the mapped types. To explicitly specify that Orika should use one particular constructor, use the constructorA(parameterNames…) and constructorB(parameterNames…), as shown below; the constructorA method is used to specify a constructor for the ‘left’ side of the mapping, and constructorB is used to specify a constructor for the ‘right’ side.
In this example, we specify that the constructor with parameters { “name”, “id” } should be used to instantiate instances of BasicPerson:
mapperFactory.classMap(BasicPerson.class, BasicPersonDto.class) .constructorA("name","id") ... .register();
Field names can refer specific elements of arrays and lists, by using the typical syntax for referencing array and list elements used by Java EL, for example, suppose we have the class structure given below:
class BasicPerson { private List<String> nameParts; // getters/setters omitted } class BasicPersonDto { private String firstName; private String lastName; // getters/setters omitted }
We could map the first element of BasicPerson’s ‘nameParts’ to BasicPersonDto’s ‘firstName’ property, and the second element to ‘lastName’, like so:
mapperFactory.classMap(BasicPerson.class, BasicPersonDto.class) .field("nameParts[0]", "firstName") .field("nameParts[1]", "lastName") .register();
Note that the same structure would work if ‘nameParts’ were defined as a `String[]`
The same pattern may be applied to refer to values in a Map by listing the key’s value within brackets, but the key value should be quoted (double or single); assuming that the ‘nameParts’ property from the previous example were defined as a `Map<String, String>`, we could reference individual key values like so:
mapperFactory.classMap(BasicPerson.class, BasicPersonDto.class) .field("nameParts['first']", "firstName") .field("nameParts[\"last\"]", "lastName") .register();
Nested properties may be referenced using standard dot ‘.’ notation to separate properties, for example, assume we have the class structure listed below:
class Name { private String first; private String last; private String fullName; // getters/setters } class BasicPerson { private Name name; // getters/setters omitted } class BasicPersonDto { private String firstName; // getters/setters omitted }
Then the following syntax could be used to map the nested “first” property of BasicPerson’s “name” to the “firstName” property of BasicPersonDto:
mapperFactory.classMap(BasicPerson.class, BasicPersonDto.class) .field("name.first", "firstName") .register();
Nested multi-occurrence elements (of Array, Collection or Map) can also be mapped, by using syntax similar to that used for referencing individual indices of Arrays, Lists or Maps. Suppose we have the class structure given below:
class Name { private String first; private String last; private String fullName; // getters/setters } class Person { private List<Name> names; // getters/setters } class PersonDto { private Map<String, Name> personalNames; private String[] firstNames; private List<String> lastNames; // getters/setters omitted }
If we wanted to map the ‘names’ property from Person into the Map ‘personalNames’ in PersonDto, with the ‘fullName’ property (of the Name element) as the key, and the Name element itself as the value, we could accomplish it with the following syntax:
mapperFactory.classMap(Person.class, PersonDto.class) .field("names{fullName}", "personalNames{key}") .field("names{}", "personalNames{value}") .register();
A few things to note: