Converter is a Library used to convert/map an object to another, in a simple way without doing making to many steps to achieve the object conversion.
This Mapper Lib inspired in C# AutoMapper Library.
// Converter Instance
IConverter converter = new Converter();
// Entities
Product model = new Product();
// Mapping
ProductDto dto = converter.map(model).to(ProductDto.class);
We can also create mapping configuration for each class type, and add global options if needed. The options can be added on Mapping Configuration Creation or after, it depends to you.
- Mapping Configuration Creation
// Converter Instance
IConverter converter = new Converter();
converter.createMap(Category.class, CategoryDto.class);
converter.createMap(Product.class, ProductDto.class, (options) -> {
options.beforeMap((src, dst) -> {
// TODO: something nice 🤩 before the object is mapped
});
options.afterMap((src, dst) -> {
// TODO: something nice 🤩 after the object is mapped
});
});
- After Mapping Configuration Creation, this way we can chain them
// Converter Instance
IConverter converter = new Converter();
converter.createMap(Category.class, CategoryDto.class);
converter.createMap(Product.class, ProductDto.class)
.beforeMap((src, dst) -> {
// TODO: something nice 🤩 before the object is mapped
})
.afterMap((src, dst) -> {
// TODO: something nice 🤩 after the object is mapped
});
- If you also want to add reverse mapping for the entities, we use reverseMap to achieve that
// Converter Instance
IConverter converter = new Converter();
converter.createMap(Category.class, CategoryDto.class)
.reverseMap();
- We can also mutate or transform a value types
// Converter Instance
IConverter converter = new Converter();
converter.addTransform(String.class, String[].class, (source) -> {
String[] arrayOfStringValue = source.split(";");
return arrayOfStringValue;
});
- Using forMember method we can target a member and modify it value, and skipMember to avoid member mapping
// Converter Instance
IConverter converter = new Converter();
converter.createMap(User.class, UserDto.class)
.forMember("name", (src) -> {
return " Sr(a)." + src.getName();
})
.forMember(UserDto::setUsername, (src) -> {
return "@" + src.getUsername();
})
.forMember("role", (src, member) -> {
// Adding extra mapping while mapping the member
// Getting just one record from the list and mapping
// From: List<Role> to: Role
UserRoleDto role = member.map(src.getRoles().get(0)).to(UserRoleDto.class);
return role;
})
.skipMember("password");
- We can also use skipTypes to avoid certain type mapping
// Converter Instance
IConverter converter = new Converter();
// We can use name of the Class
converter.skipTypes("Float");
// Or, we can use the Class Type
converter.skipTypes(String.class);
// Entities
Product model = new Product();
// Mapping
ProductDto dto = converter.map(model).to(ProductDto.class, (options) -> {
options.skipTypes(Integer.class);
});
Note: When creating forMember
map expression with inner mapping, always use memberMapping
from
the second argument
of the expresion: .forMember("field", (src, memberMapping) -> { })
.
We cannot use the main converter
instance because whenever converter.map({})
is called,
it creates a new instance of MappingProcessor, ignoring the previous configuration.
- Converting and modifying
// Converter Instance
IConverter converter = new Converter();
// Entities
Product model = new Product();
// Mapping
ProductDto dto = converter.map(model).to(ProductDto.class, (options) -> {
options.beforeMap((src, dst) -> {
// TODO: something nice 🤩 before the object is mapped
});
options.afterMap((src, dst) -> {
// TODO: something nice 🤩 after the object is mapped
});
});
- Mapping or Extracting values from another object
// Converter Instance
IConverter converter = new Converter();
// Entities
Product dbModel = new Product();
dbModel.setName("Coca Cola");
dbModel.setPrice(0.5f);
// Mapping
ProductDto dtoModel = new ProductDto();
dtoModel.setName("Sprite");
dtoModel.setPrice(1f);
Product dbModelMapped = converter.map(dbModel).from(dtoModel);
// Has the same memory address
Boolean isEquals = dbModel == dbModelMapped;
- Making a copy of an Object with different memory address
// Converter Instance
IConverter converter = new Converter();
// Entities
Product model = new Product();
model.setName("Coca Cola");
model.setPrice(0.5f);
Product copy = converter.map(model).to();
// Has the different memory address
Boolean isEquals = dbModel == dbModelMapped;
Note: We can also apply modifiers, like: .to((options) -> { })
- Members can be skipped while extracting values from another object using mapping actions
// Converter Instance
IConverter converter = new Converter();
// Entities
Product dbModel = new Product();
dbModel.setName("Coca Cola");
dbModel.setPrice(0.5f);
// Mapping
ProductDto dtoModel = new ProductDto();
dtoModel.setName("Sprite");
dtoModel.setPrice(1f);
Product dbModelMapped = converter.map(dbModel).from(dtoModel, (options) -> {
options.skipMembers("price");
// Note: We advice to use this one
// The other one target every property having the same name even in their child
// options.skipMembers(Product.class.getDeclaredField("price"));
});
- Converting and modifying a list
// Converter Instance
IConverter converter = new Converter();
// Entities
Product model1 = new Product("Coca Cola");
Product model2 = new Product("Sprite");
List<Product> models = Arrays.asList(model1, model2);
// Mapping
List<ProductDto> dto = converter.map(models).to(ProductDto.class, (options) -> {
options.beforeMap((src, dst) -> {
// TODO: something nice 🤩 before the object is mapped
// src -> List<Product>
// dst -> List<ProductDto> <null>
});
options.afterMap((src, dst) -> {
// TODO: something nice 🤩 after the object is mapped
// src -> List<Product>
// dst -> List<ProductDto>
});
options.beforeEachMap((src, dst) -> {
// TODO: something nice 🤩 before the object is mapped
// src -> item: Product
// dst -> item: ProductDto <null>
});
options.afterEachMap((src, dst) -> {
// TODO: something nice 🤩 after the object is mapped
// src -> item: Product
// dst -> item: ProductDto
});
});
If you use SpringBoot and want to use Dependency Injection, you can create a converter config file and assign it as @Component:
@Component
public class ConverterConfig extends Converter {
public ConverterConfig() {
createMap(Product.class, ProductDto.class);
createMap(User.class, UserDto.class)
.skipMember("password");
}
@Bean @Primary
public Converter autowire() throws InstantiationException, IllegalAccessException {
// This method is used to give the possibility to
// instantiate the class using @Autowired annotation
return this;
}
}
- Injecting in the controller via DI
@Component
public class ProductController {
@Autowired
IConverter converter;
@Autowired
ProductService service;
@GetMapping(produces = "application/json", value = "/{id}")
public ProductDto getOne(@PathVariable("id") Long id) {
Product product = service.findById(id);
ProductDto productDto = converter.map(product).to(ProductDto.class);
return productDto;
}
@GetMapping(produces = "application/json")
public List<ProductDto> getAll() {
List<Product> products = service.findAll();
List<ProductDto> productDtos = converter.map(products).to(ProductDto.class);
return productDtos;
}
}
To load the dependency to you a Maven project, you can follow these steps:
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
...
<dependency>
<groupId>io.github.afonsomatelias</groupId>
<artifactId>converter</artifactId>
<version>[tag]</version>
</dependency>
</dependencies>
Note: Converter tags begins with v[number]. Example: v.1.1
mvn clean install