jmapper / Wiki / Examples (2024)

A POJO to POJO Java Mapper

Brought to you by:revittorio76

  • Summary
  • Files
  • Reviews
  • Support
  • Wiki
  • Code

Menu▾▴

  • Wiki Home
  • Browse Pages
  • Browse Labels
  • Formatting Help

Examples

Hello-world Sample

In order to explain how to use JMapper, let's start with a very simply example.

Suppose you have a project with 2 layer: Middleware and Front-end.
For each layer you want to define a specific object with layer-related characteristics; for example, front-end object have all fields represented as String in order to facilitate the visualization of the data but in middleware you want to use correct formats in order to manipulate data correctly.
(For this example we do not considerate how data is stored; it's simply another layer and mapping could work in the same way).

Suppose we are manipulating a Person, so we could have 2 person objects, one for MW and one for FE:

@Mappingpublic class PersonMW { private long code; private String surname; private String name; private Gender gender; private Date birthDate; //.... constructor - getter/setter - toString - hashCode/equals standard methods}public enum Gender { Male, Female;}

and

public class PersonFE { private String code; private String surname; private String name; private String gender; private String birthDate; //.... constructor - getter/setter - toString - hashCode/equals standard methods}

NOTE that there is ONLY one annotation in PersonMW object.

You can map from PersonMW to PersonFE and vice versa with this only row:

session.map(personFe, PersonMW.class);

Following a JUnit sample to test both conversions:

 @Test public void testPerson() { PersonMW personMw = personService.getPerson(1); Assert.assertNotNull(personMw); System.out.println("testPerson: personMw="+personMw); PersonFE personFe; try { personFe = MapperUtils.Instance.getSession().map(personMw, PersonFE.class); } catch (JMapperConversionException e) { e.printStackTrace(); throw new RuntimeException("Mapping error 1", e); } Assert.assertNotNull(personFe); System.out.println("testPerson: personFe="+personFe); Assert.assertEquals(personMw.getGender().name(), personFe.getGender()); PersonMW personMw2; try { personMw2 = MapperUtils.Instance.getSession().map(personFe, PersonMW.class); } catch (JMapperConversionException e) { e.printStackTrace(); throw new RuntimeException("Mapping error 2", e); } Assert.assertNotNull(personMw2); System.out.println("testPerson: personMw2="+personMw2); Assert.assertEquals(personMw,personMw2); }

The meaning of the annotation at the beginning of PersonMW class is:
enable mapping of PersonMW objects from any kind of object and to any kind of object, mapping all fields one-by-one, searching corresponding fields in opposite class by name and converting them using default primitive conversions.

The result in JUnit test is this one:

suppose that personService implementation is something like following:

public class PersonService { public PersonMW getPerson(long code){ return new PersonMW( code, "Obama", "Barack", Gender.Male, buildDate(4, 8, 1961, 15, 37, 45)); } private Date buildDate(int day, int month, int year, int hour, int minute, int second) { //... build a java.util.Date object }}

the test will write in stdout following rows:

testPerson: personMw=PersonMW [code=1, surname=Obama, name=Barack, gender=Male, birthDate=Tue Nov 12 15:37:45 CET 9]testPerson: personFe=PersonFE [code=1, surname=Obama, name=Barack, gender=Male, birthDate=12/11/0009 15:37:45]testPerson: personMw2=PersonMW [code=1, surname=Obama, name=Barack, gender=Male, birthDate=Tue Nov 12 15:37:45 CET 9]

A bit complex Sample

Let's try to map a more complex structure.

Suppose we have a person object like this:

@Mapping(source=PersonFE_3.class, target=PersonFE_3.class)public class PersonMW_3 { private long code; private String surname; private String name; private Gender_2 gender; @MappingField(mapper="Full date format") private Date birthDate; @MappingField(mapper="Short date format") private Date registrationDate; @FieldMappings({ @MappingField(direction=MappingDirection.Outgoing, value="lastLoginYear", mapper="Format date", param="yyyy"), @MappingField(direction=MappingDirection.Outgoing, value="lastLoginMonth", mapper="Format date", param="MM"), @MappingField(direction=MappingDirection.Outgoing, value="lastLoginDay", mapper="Format date", param="dd"), @MappingField(direction=MappingDirection.Incoming, value="this", mapper="Build Date", param={"lastLoginYear","yyyy","lastLoginMonth","MM","lastLoginDay","dd"}) }) private Date lastLoginDate; @FieldMappings({ @MappingField(direction=MappingDirection.Outgoing, param={"java.util.ArrayList","test.jmapper.helloworld.structures.fe.AddressFE_3"}), @MappingField(direction=MappingDirection.Incoming, param={"java.util.HashSet","test.jmapper.helloworld.structures.mw.AddressMW_3"}) }) private Set<AddressMW_3> addresses; @NotMapped private Date objectCreationDate = new Date(); //.... constructor - getter/setter - toString - hashCode/equals standard methods}@Mapping(source=Integer.class, target=Integer.class, mapNotAnnotatedFields=false)public enum Gender_2 { Male(1), Female(2); private final int code; private Gender_2(int code) { this.code = code; } @ConversionMethod public Integer getCode(){ return code; } @BuildMethod public static Gender_2 fromCode(Integer code){ for (Gender_2 elem : Gender_2.values()) { if(elem.getCode()==code) return elem; } throw new RuntimeException("Unknown code "+code); }}@Mappingpublic class AddressMW_3 { private long code; private String city; private String street; private int number; //.... constructor - getter/setter - toString - hashCode/equals standard methods}

and corresponding FE objects

public class PersonFE_3 { private Long code; private String surname; private String name; private Integer gender; private String birthDate; private String registrationDate; private String lastLoginYear; private String lastLoginMonth; private String lastLoginDay; private List<AddressFE_3> addresses; //.... constructor - getter/setter - toString - hashCode/equals standard methods}public class AddressFE_3 { private Long code; private String city; private String street; private String number; //.... constructor - getter/setter - toString - hashCode/equals standard methods}

NOTE all the annotations in all MW objects

Mapping code to switch from PersonMW_3 and PersonFE_3 and vice versa will be

session.map(personFe, PersonMW_3.class);

that is exacly the same as above.

The only things that changes are mapping annotations; used annotations have following meanings:

  • @Mapping: in this example this annotation allows mapping only between PersonMW_3 and PersonFE_3 kind of objects (in previous example it was from PersonMW to any kind of Objects)
  • @MappingField(mapper="Full date format"): use a specific mapper to map this date (not the default one). This mapping have tha name "Full date format".
  • @FieldMappings: specify a list of @MappingField, all used with the same field; this annotation is used when a field is mapped in more that one fields in other class or when incoming mapping differs from outgoing mapping (see addresses field)
  • @MappingField(direction=MappingDirection.Outgoing, value="lastLoginYear", mapper="Format date", param="yyyy"): attribute means are:
    • direction: specify mapping direction; in this case mapping is used only in OUTGOING mapping (from PersonMW_3 to PersonFE_3)
    • value: corresponding remote field name (different from current one, used by default)
    • mapper: mapper name used to map this field to specified remote one
    • param: parameters passed to specified mapper
  • @NotMapped: this field won't be mapped
  • @ConversionMethod: indicate that this method will be use to instantiate remote class (insthead of constructor)
  • @BuildMethod: indicate that this method will be use to instantiate current class (insthead of constructor)

To complete mapping there are something missing: specific mapping used in these classes.
In above example, we have used following specific mapping:

  • Full date format
  • Short date format
  • Format date
  • Build Date

These are specific mapping so we have to define them.
We will define all of them as Converter methods inside a Converter Class:

@Converterpublic class DateConverter implements Serializable { private static final long serialVersionUID = -1381822225120226227L; private static final String DATE_SEPARATOR = "/"; private static final DateFormat SHORT_CONVERTER = new SimpleDateFormat("dd/MM/yyyy"); private static final DateFormat FULL_CONVERTER = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss.SSS"); @ConverterMethod("Short date format") public Date fromStringShort(String value) throws ParseException { return SHORT_CONVERTER.parse(value); } @ConverterMethod("Short date format") public String toStringShort(Date value) { return SHORT_CONVERTER.format(value); } @ConverterMethod("Full date format") public Date fromStringFull(String value) throws ParseException { return FULL_CONVERTER.parse(value); } @ConverterMethod("Full date format") public String toStringFull(Date value) { return FULL_CONVERTER.format(value); } /** * Require one single parameter with the format of the return value  * Eg.: "yyyy" or "MM/yyyy" *  * @param value date to convert * @param param format of the return value (see SimpleDateFormat) * @return formatted date * @see java.text.SimpleDateFormat */ @ConverterMethod("Format date") public String getDateField(Date value, String[] param) { SimpleDateFormat format = new SimpleDateFormat(param[0]); return format.format(value); } /** * Require pairs of parameters with, for each pair, following values: * - name of the field/method in input object to use * - format of the part of date corresponding to the field * Eg.: "year","yyyy","month","MM","day","dd" *  * @param o object containing field to convert * @param param parameters of conversion * @return converted Date * @throws ParseException  * @see java.text.SimpleDateFormat */ @ConverterMethod(value="Build Date", allowSubClasses=AllowSubClasses.Source) public Date buildDate(Object o, String[] param) throws ParseException { if(param.length>1) { StringBuilder sourceBuilder = new StringBuilder(getValue(o, param[0]).toString()); StringBuilder formatBuilder = new StringBuilder(param[1]); for(int i=2;i<(param.length-1);i+=2) { sourceBuilder.append(DATE_SEPARATOR) .append(getValue(o, param[i])); formatBuilder.append(DATE_SEPARATOR) .append(param[i+1]); } SimpleDateFormat format = new SimpleDateFormat(formatBuilder.toString()); return format.parse(sourceBuilder.toString()); } throw new RuntimeException("Parameters Error"); } private Object getValue(Object o, String name) { try { Field field = o.getClass().getDeclaredField(name); if(!field.isAccessible()) field.setAccessible(true); return field.get(o); } catch (IllegalArgumentException e) { e.printStackTrace(); throw new RuntimeException("Error retrieving field "+name); } catch (SecurityException e) { e.printStackTrace(); throw new RuntimeException("Error retrieving field "+name); } catch (IllegalAccessException e) { e.printStackTrace(); throw new RuntimeException("Error retrieving field "+name); } catch (NoSuchFieldException e) { e.printStackTrace(); throw new RuntimeException("Error retrieving field "+name); } }}

NOTE some small things in converter methods:
A Converter Class must implements Serializable, because it can be serialized in internal JMapper caches
Converter Methods first parameter is Original Object to convert
* Converter Methods can define other 3 parameters that will be auto-wired from JMapper:
* Class: destination class type
* JMapperSession: mapping session in which mapping is done
* String[]: mapping parameters

the user can add after first parameter one or more of these kind of objects in any order and use them to implements mapping.
For Example:
destination class can be used to instantiate destination class in the cases in which mapping does not know which class to instantiate or in which cases user can choose in a range of different destination classes;
mapping session can be used to invoke recursively map(...) method in sub-fields or to retrieve some properties
mapping parameters (as in above examples) to leave user the possibility to pass some properties to mapping, like as, for example, date format in date conversions.

The result of following JUnit test is this one:

 @Test public void testPerson_3() { PersonMW_3 personMw = personService.getPerson_3(1); Assert.assertNotNull(personMw); System.out.println("testPerson: personMw="+personMw); try { MapperUtils.Instance.getSession().addConverterClass(DateConverter.class); } catch (JMapperDefinitionException e) { e.printStackTrace(); throw new RuntimeException("Mapping error 0", e); } PersonFE_3 personFe; try { personFe = MapperUtils.Instance.getSession().map(personMw, PersonFE_3.class); } catch (JMapperConversionException e) { e.printStackTrace(); throw new RuntimeException("Mapping error 1", e); } Assert.assertNotNull(personFe); System.out.println("testPerson: personFe="+personFe); Assert.assertEquals(personMw.getGender().getCode(), personFe.getGender()); Object personMw2; try { personMw2 = MapperUtils.Instance.getSession().map(personFe, "Mapping"); } catch (JMapperConversionException e) { e.printStackTrace(); throw new RuntimeException("Mapping error 2", e); } Assert.assertNotNull(personMw2); System.out.println("testPerson: personMw2="+personMw2); Assert.assertNotSame(personMw,personMw2); Assert.assertEquals(personMw,personMw2); Assert.assertTrue(!personMw.getObjectCreationDate().equals(((PersonMW_3)personMw2).getObjectCreationDate())); AddressMW_3 addressMw = personMw.getAddresses().iterator().next(); AddressMW_3 addressMw2 = null; for(AddressMW_3 addressMw2It : ((PersonMW_3)personMw2).getAddresses()) { if(addressMw.getCode()==addressMw2It.getCode()){ Assert.assertNull(addressMw2); addressMw2 = addressMw2It; } } Assert.assertNotNull(addressMw2); Assert.assertNotSame(addressMw,addressMw2); Assert.assertEquals(addressMw,addressMw2); }

suppose that personService implementation is something like following:

public class PersonService { public PersonMW_3 getPerson_3(long code) { return new PersonMW_3( code, "Obama", "Barack", Gender_2.Male, buildDate(4, 8, 1961, 15, 37, 45, 125), buildDate(4, 11, 2008), buildDate(23, 9, 2012), getAddresses(code)); } private Set<AddressMW_3> getAddresses(long code) { Set<AddressMW_3> addresses = new HashSet<AddressMW_3>(); long acode=code+1; addresses.add(new AddressMW_3(acode++, "Milano", "Via Roma", 73)); addresses.add(new AddressMW_3(acode++, "Roma", "Via Armata", 3)); addresses.add(new AddressMW_3(acode++, "Napoli", "Via XVII Settembre", 1)); return addresses; } private Date buildDate(int day, int month, int year, int hour, int minute, int second) { //... build a java.util.Date object }}

the test will write in stdout following rows:

testPerson: personMw=PersonMW_3 [code=1, surname=Obama, name=Barack, gender=Male, birthDate=Fri Aug 04 15:37:45 CET 1961, registrationDate=Mon Mar 31 00:00:00 CET 10, lastLoginDate=Tue Feb 01 00:00:00 CET 29, addresses=[AddressMW_3 [code=3, city=Roma, street=Via Armata, number=3], AddressMW_3 [code=2, city=Milano, street=Via Roma, number=73], AddressMW_3 [code=4, city=Napoli, street=Via XVII Settembre, number=1]], objectCreationDate=Wed Oct 03 16:23:13 CEST 2012]testPerson: personFe=PersonFE_3 [code=1, surname=Obama, name=Barack, gender=1, birthDate=04/08/1961 15:37:45.125, registrationDate=31/03/0010, lastLoginYear=0029, lastLoginMonth=02, lastLoginDay=01, addresses=[AddressFE_3 [code=3, city=Roma, street=Via Armata, number=3], AddressFE_3 [code=2, city=Milano, street=Via Roma, number=73], AddressFE_3 [code=4, city=Napoli, street=Via XVII Settembre, number=1]]]testPerson: personMw2=PersonMW_3 [code=1, surname=Obama, name=Barack, gender=Male, birthDate=Fri Aug 04 15:37:45 CET 1961, registrationDate=Mon Mar 31 00:00:00 CET 10, lastLoginDate=Tue Feb 01 00:00:00 CET 29, addresses=[AddressMW_3 [code=3, city=Roma, street=Via Armata, number=3], AddressMW_3 [code=2, city=Milano, street=Via Roma, number=73], AddressMW_3 [code=4, city=Napoli, street=Via XVII Settembre, number=1]], objectCreationDate=Wed Oct 03 16:23:14 CEST 2012]
jmapper / Wiki / Examples (2024)

FAQs

What is JMapper? ›

JMapper is the mapping framework that aims to provide an easy-to-use, high-performance mapping between Java Beans. The framework aims to apply the DRY principle using Annotations and relational mapping.

What is a ModelMapper? ›

ModelMapper is a Java library that helps in mapping objects from one model to another, reducing the need for manual mapping code. It is a highly configurable library that uses conventions to automatically map objects, but can also be configured to provide custom mapping rules.

Does Jmapper use reflection? ›

I know, that Orika and Jmapper are great libraries from Google and they use reflection in a different way than for example Dozer, which is much slower, they se reflection to generete code somehow..

Which is better, MapStruct or ModelMapper? ›

Ease of use: ModelMapper is known for its simplicity and ease of use. It automatically maps fields with the same names and data types. On the other hand, MapStruct requires developers to write explicit mapping interfaces, which can lead to more initial setup but offers more control over the mapping process.

Is it safe to use ModelMapper? ›

Refactoring Safe

ModelMapper provides a simple, fluent mapping API for handling special use cases. The API is type-safe and refactoring-safe, using actual code, rather than string references, to map properties and values.

What is Dozer Mapper vs ModelMapper? ›

Dozer is a Java Bean to Java Bean mapper that recursively copies data from one object to another. ModelMapper is an intelligent object mapping library that automatically maps objects to each other.

Does ModelMapper use setters? ›

In addition to mapping properies using getters and setters, ModelMapper supports the mapping of source property paths defined as Strings: map().

What is the difference between Dozer Mapper and ModelMapper? ›

Dozer is a Java Bean to Java Bean mapper that recursively copies data from one object to another. ModelMapper is an intelligent object mapping library that automatically maps objects to each other.

Why do we use mapper in Java? ›

In general, you use mappers in programming to improve code organization, maintainability, and testability by encapsulating the logic of converting data between different representations. They help separate concerns, making your codebase more modular and easier to understand.

What is the difference between MapStruct and Dozer Mapper? ›

Dozer is using reflection mechanism for mapping. Mapping rules are defined in Java files, mostly in annotations. Based on this rules MapStruct generates Java classes, which are then used for mapping. Dozer uses reflection mechanism for mapping, which means it is slower than MapStruct.

What is the difference between MapStruct and Jackson? ›

Support for Mapping Types: Jackson provides comprehensive support for mapping various types, including collections, generics, and nested objects. It can handle complex mappings with relative ease. On the other hand, MapStruct focuses primarily on mapping between Java Beans and DTOs (Data Transfer Objects).

Top Articles
Latest Posts
Article information

Author: Kerri Lueilwitz

Last Updated:

Views: 5933

Rating: 4.7 / 5 (67 voted)

Reviews: 82% of readers found this page helpful

Author information

Name: Kerri Lueilwitz

Birthday: 1992-10-31

Address: Suite 878 3699 Chantelle Roads, Colebury, NC 68599

Phone: +6111989609516

Job: Chief Farming Manager

Hobby: Mycology, Stone skipping, Dowsing, Whittling, Taxidermy, Sand art, Roller skating

Introduction: My name is Kerri Lueilwitz, I am a courageous, gentle, quaint, thankful, outstanding, brave, vast person who loves writing and wants to share my knowledge and understanding with you.