Only this pageAll pages
Powered by GitBook
1 of 21

ShapeShift

Loading...

Introduction

Loading...

Loading...

API Documentation

Loading...

Loading...

Loading...

Features

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Guides

Loading...

Loading...

Loading...

Loading...

Loading...

Installation

Adding ShapeShift to your project.

Installation

To get started with ShapeShift, install the core library via Maven or Gradle:

Maven

Gradle

Groovy DSL

Kotlin DSL

Requirements

  • Minimum supported Kotlin version: 1.6.X

  • Minimum supported Java version: 1.8

<dependency>
  <groupId>dev.krud</groupId>
  <artifactId>shapeshift</artifactId>
  <version>0.8.0</version>
</dependency>
implementation 'dev.krud:shapeshift:0.8.0'
implementation("dev.krud:shapeshift:0.8.0")

Overview

ShapeShift – A lightweight, modular, performant and extensible object mapping library for Kotlin/Java.

ShapeShift is a Kotlin first object mapping library. We have built ShapeShift because we wanted a simple to use, minimal boiler plate mapping engine, that is also flexible and supports the most advanced use cases.

Built with Kotlin in mind, ShapeShift was designed around its ecosystem and best practices. The library APIs for mapping with Kotlin:

  • Annotations - Fully featured annotation based mapping, just add annotations to your objects and ShapeShift handles the rest. Including using custom field transformers, conditional mapping, advanced object decoration and much more.

  • - Allowing you to define the relations between objects. This allows you to map objects you can't change (or don't want to), like objects from 3rd party libraries. Additionally you can define inline transformations, conditions and decorations, enabling deep customization and very advanced mapping.

ShapeShift also has full Java support available with multiple APIs for mapping:

  • - Same API as Kotlin with full Java support.

  • - The equivalent Java API for the Kotlin DSL. Allowing you to define the relations between objects without modifying their code, and map objects you can't change (or don't want to).

ShapeShift main features:

  • Auto mapping

  • Custom field transformers

  • Default transformers

  • Deep mapping

Check out the quick start guide to learn how get started:

Instance Mapping

Mapping to destination classes without a no arg constructor.

Due to the fact that ShapeShift uses reflection behind the scenes, destination classes need a no arg constructor. But in some cases you have no control over the destination classes and cannot modify them to add a no arg constructor. This is where Instance Mapping comes into play, you can pass already-instantiated destination objects to the map method.

Version 0.0.7 of ShapeShift introduced new Object Suppliers functionality as an improved solution for the no arg constructor issue.

@DefaultMappingTarget(SimpleEntityDisplay::class)
data class SimpleEntity(
    @MappedField
    val name: String,
    @MappedField
    val description: String,
    val privateData: String
@DefaultMappingTarget(SimpleEntityDisplay.class)
public class SimpleEntity {
    @MappedField
    private String name;
    @MappedField
    private String description;
    private

SimpleEntityDisplay does not have a no arg constructor. You can either add a no arg constructor or initiate a new instance of SimpleEntityDisplay and pass it to the map function.

Object Suppliers

Mapping to destination classes without a no arg constructor.

Due to the fact that ShapeShift uses reflection behind the scenes, destination classes need a no arg constructor. But in some cases you have no control over the destination classes and cannot modify them to add a no arg constructor. This is where Object Suppliers comes into play, you can register object suppliers to the ShapeShift instance to add your own logic for instance generation.

is another solution for the no arg constructor issue. The advantage of Object Suppliers is that they also work when mapping classes containing subclasses while Instance Mapping only solves the issue for the top level class instance.

We have two classes, the source class SimpleEntity and the destination class SimpleEntityDisplay

Android Usage

Using ShapeShift with an Android project.

In this guide we will see how we can use ShapeShift in an Android project.

Installation

Include the following dependency in your build.gradle file.

Mapping Strategy

Mapping behavior for null values.

Use the mapping strategy to define basic mapping behavior for null values.

Options

The following options are available for mapping strategy:

String
privateData;
// Getters and Setters...
}
)
data class SimpleEntityDisplay(
    val name: String,
    val description: String
)
public class SimpleEntityDisplay {
    private String name;
    private String description;

    public SimpleEntityDisplay(String name, String description) {
        this.name = name;
        this.description = description;
    }
    // Getters and Setters...
}
val shapeShift = ShapeShiftBuilder().build()
val simpleEntity = SimpleEntity("test", "test description", "private data")
val simpleEntityDisplay = SimpleEntityDisplay("", "")
// Passing simpleEntityDisplay as a destination instance 
val result = shapeShift.map(simpleEntity, simpleEntityDisplay)
ShapeShift shapeShift = new ShapeShiftBuilder().build();
SimpleEntity simpleEntity = new SimpleEntity();
simpleEntity.setName("test");
simpleEntity.setDescription("test description");
simpleEntity.setPrivateData("private data");
SimpleEntityDisplay simpleEntityDisplay = new SimpleEntityDisplay("", "");
// Passing simpleEntityDisplay as a destination instance 
SimpleEntityDisplay result = shapeShift.map(simpleEntity, simpleEntityDisplay);
Usage

All of ShapeShift's features are available in Android. Continue to the Quick Start guide or jump directly to the Annotations, Kotlin DSL or Java Builder APIs.

ShapeShift uses reflection for its mapping functionality. If ProGuard is enabled in the project then mapped models should be excluded from obfuscation in order for the mapping to work.

implementation 'dev.krud:shapeshift:0.8.0'
// Add Maven Central to your repositories if needed
repositories {
    mavenCentral()
}
MAP_ALL
- A strategy that maps all values of a field.
  • MAP_NOT_NULL - A strategy that maps only the values of a field that are not null. If the field value is null it will not override the current value in the target class instance. This is the default strategy when creating a new ShapeShift instance.

  • Usage

    There are two options for setting the mapping strategy:

    Default Mapping Strategy

    Settings the default mapping strategy for the ShapeShift instance.

    val shapeShift = ShapeShiftBuilder()
        .withDefaultMappingStrategy(MappingStrategy.MAP_ALL)
        .build()
    ShapeShift shapeShift = new ShapeShiftBuilder()
            .withDefaultMappingStrategy(MappingStrategy.MAP_ALL)
            .build();

    Override Mapping Strategy

    Overriding the mapping strategy for a specific field.

    Annotations

    Kotlin DSL

    Java Builder

    @MappedField(overrideMappingStrategy = MappingStrategy.MAP_ALL)
    @MappedField(overrideMappingStrategy = MappingStrategy.MAP_ALL)
    val mapper = mapper<From, To> {
        From::value mappedTo To::value overrideStrategy MappingStrategy.MAP_NOT_NULL
    }
    MappingDefinition mappingDefinition = new MappingDefinitionBuilder(From.class, To.class)
            .mapField("value", "value").withMappingStrategy(MappingStrategy.MAP_NOT_NULL)
            .build();

    Multiple mapping targets

  • Conditional mapping

  • Mapping decorators

  • Seamless spring integration

  • Native Android support

  • Kotlin DSL
    Annotations
    Java Builder
    Quick Start
    .
    @DefaultMappingTarget(SimpleEntityDisplay::class)
    data class SimpleEntity(
        @MappedField
        val name: String,
        @MappedField
        val description: String,
        val privateData: String
    
    @DefaultMappingTarget(SimpleEntityDisplay.class)
    public class SimpleEntity {
        @MappedField
        private String name;
        @MappedField
        private String description;
        private
    

    The destination class does not have a no arg constructor.

    data class SimpleEntityDisplay(
        val name: String,
        val description: String
    )
    public class SimpleEntityDisplay {
        private String name;
        private String description;
    
        public SimpleEntityDisplay(String name, String description) {
            this.
    

    To solve this issue we need to either use instance mapping or add an object supplier for the class.

    Adding Object Suppliers

    Adding object suppliers is available through the ShapeShiftBuilder class. Object suppliers can be added inline or as a separate class.

    Class Object Suppliers

    To create an object supplier class implement the Supplier interface.

    And register it to the ShapeShift instance.

    Inline Object Suppliers

    It is also possible to add the object supplier logic inline.

    Mapping with Object Suppliers

    Now that we added an object supplier for the SimpleEntityDisplay class we can map to it as if it has a no arg constructor.

    Instance Mapping

    Spring Usage

    Using ShapeShift with a Spring Boot project.

    In this guide we will see how we can use ShapeShift's Spring Boot starter to seamlessly add transformers and decorators within Spring projects.

    The Spring Boot starter automatically registers mapping transformer beans and decorator beans, as well as a customizer for cases where further customization of the ShapeShift instance is required.

    Installation

    Install the spring library via Maven or Gradle:

    Maven

    Gradle

    Groovy DSL

    Kotlin DSL

    Registering Transformers

    In order to register a new transformer, simply create the class and register it as a Spring Bean via your method of choice. (JavaConfig, XML)

    Annotation Example

    JavaConfig Example

    Default transformers cannot be registered in this way, see below on how to achieve this

    Registering Decorators

    You can register decorators in the same way that you register transformers, simply create the class and register it as a Spring Bean via your method of choice. (JavaConfig, XML)

    Annotation Example

    JavaConfig Example

    Using Customizers

    If you need to be able to affect the ShapeShift instance in ways that are not mentioned above, you can implement a ShapeShiftBuilderCustomizer, customizers are configuration classes that give you access to the ShapeShiftBuilder for the main Spring instance of ShapeShift. To use, simply implement the interface and annotate with @Configuration. In customize, you can do anything you're normally able to do with the standard ShapeShiftBuilder

    Kotlin DSL

    Kotlin DSL based usage of the ShapeShift library.

    Basic Mapping

    We start by defining two classes, our source class SimpleEntity and our destination class SimpleEntityDisplay.

    We can now create a simple mapper. In this example, we want to map the name and description fields of SimpleEntity

    Conditions

    Conditional mapping of fields by predicates.

    Conditions are used to determine wether a field should be mapped according to certain logic. In some use cases it is required to map a field from the source class only if some predicate is true, condition is that predicate.

    We start with our two classes, our source class SimpleEntity and our destination class SimpleEntityDisplay.

    Decorators

    Decorate mapping operations with additional logic.

    In some use cases mapping fields is not enough. Sometimes we need to add additional logic to the mapping. For these use cases we have the decorators, the decorators allow us to perform operations on our models after the mapping has finished.

    We start with our two classes, our source class User and our destination class UserDisplay.

    In this example, we want to merge the firstName

    class SimpleEntityDisplaySupplier : Supplier<SimpleEntityDisplay> {
        override fun get(): SimpleEntityDisplay {
            return SimpleEntityDisplay("", "")
        }
    }
    public class SimpleEntityDisplaySupplier implements Supplier<SimpleEntityDisplay> {
        @Override
        public SimpleEntityDisplay get() {
            return new SimpleEntityDisplay("", "");
        }
    }
    val shapeShift = ShapeShiftBuilder()
        .withObjectSupplier(SimpleEntityDisplaySupplier())
        .build()
    ShapeShift shapeShift = new ShapeShiftBuilder()
            .withObjectSupplier(new SimpleEntityDisplaySupplier(), SimpleEntityDisplay.class)
            .build();
    val shapeShift = ShapeShiftBuilder()
        .withObjectSupplier { SimpleEntityDisplay("", "") }
        .build()
    ShapeShift shapeShift = new ShapeShiftBuilder()
            .withObjectSupplier(() -> new SimpleEntityDisplay("", ""), SimpleEntityDisplay.class)
            .build();
    val simpleEntity = SimpleEntity("test", "test description", "private data")
    val simpleEntityDisplay = SimpleEntityDisplay("", "")
    val result = shapeShift.map<SimpleEntityDisplay>(simpleEntity)
    SimpleEntity simpleEntity = new SimpleEntity();
    simpleEntity.setName("test");
    simpleEntity.setDescription("test description");
    simpleEntity.setPrivateData("private data");
    SimpleEntityDisplay result = shapeShift.map(simpleEntity, SimpleEntityDisplay.class);
    )
    String
    privateData;
    // Getters and Setters...
    }
    name
    =
    name;
    this.description = description;
    }
    // Getters and Setters...
    }
    Using Customizers
    to the
    name
    and
    description
    fields of the
    SimpleEntityDisplay
    class, but not the
    privateData
    field.

    To instantiate ShapeShift we use the ShapeShiftBuilder and register our mapper:

    All that's left is to map the SimpleEntity instance to the SimpleEntityDisplay class.

    Mapping Fields

    In the example above we did basic mapping between fields. But what if we want to map from/to deep fields of child classes?

    In order to access child classes we can use the .. operator. Let's look at the following example:

    We want to map the value field in Child class inside the From class to the childValue field in the To class. We will create a mapper with the .. operator.

    The .. operator is supported in both source and destination fields, it also supports multi level depth.

    To access the grand child field we just use the .. operator twice.

    Transformers

    Field transformers are a way to transform a field from one type to another when mapping it to a destination class. More about the ins-and-outs of transformers is available here:

    The withTransformer function has 2 options to use transformers. Let's look at the following classes.

    We want to map the commaDelimitedString field to the stringList field and change the field type from String to List<String> while doing so. To accomplish that we will use a transformer.

    Class Transformer

    Our first option is to create a transformer class, StringToListMappingTransformer;

    All we need to do to use our transformer is to pass it to the withTransformer function.

    Transformers must be registered to the ShapeShift instance in order to be used. More info about registering transformers is available in the transformers page.

    Inline Transformer

    Our second option is to use an inline transformer. When we don't need to reuse a transformer we can just add its logic to the DSL.

    Auto Mapping

    Auto mapping is used to reduce the amount of boiler-place code required to configure mapping between two classes. More info about auto mapping is available here:

    Auto mapping can be added using the autoMap function.

    autoMap function receives the desired auto mapping strategy. It is possible to add any manual mapping to add/change mapping behavior.

    Mapping Condition

    Conditions are used to determine wether a field should be mapped according to certain logic. More info about conditions is available here:

    Let's look at the following classes.

    We want to map the name field only if it's not null or blank. The withCondition function has 2 options to add conditions.

    Class Condition

    Our first option is to create a condition class. The condition receives context with the original value of the field and checks that it is not null or blank.

    We will create our mapper and add the condition.

    Inline Condition

    Our second option is to use an inline condition. When we don't need to reuse a condition we can just add its logic to the DSL.

    Decorators

    Decorators allow to add additional logic to the mapping operation. More info about conditions is available here:

    Let's look at the following classes.

    We want to merge the firstName and lastName fields to the fullName field in addition to mapping them to their respectable fields.

    Decorators can be added inline or as a separate class.

    Class Decorators

    To create a decorator class implement the MappingDecorator interface.

    And register it to the ShapeShift instance.

    Inline Decorators

    It is also possible to add the decorator logic inline.

    Object Suppliers

    Due to the fact that ShapeShift uses reflection behind the scenes, destination classes need a no arg constructor. But in some cases you have no control over the destination classes and cannot modify them to add a no arg constructor. This is where Object Suppliers comes into play, you can register object suppliers to the ShapeShift instance to add your own logic for instance generation.

    More info about Object Suppliers is available here:

    Override Mapping Strategy

    The overrideStrategy function allows you to override the default mapping strategy configured on the ShapeShift instance.

    More info about mapping strategy is available here:

    Transformers
    Transformers
    Auto Mapping
    Conditions
    Decorators
    Object Suppliers
    Mapping Strategy
    <dependency>
      <groupId>dev.krud</groupId>
      <artifactId>spring-boot-starter-shapeshift</artifactId>
      <version>0.8.0</version>
    </dependency>
    implementation 'dev.krud:spring-boot-starter-shapeshift:0.8.0'
    implementation("dev.krud:spring-boot-starter-shapeshift:0.8.0")
    @Component
    class BeanTransformer : MappingTransformer<String, String> {
        override fun transform(context: MappingTransformerContext<out String>): String? {
            return context.originalValue?.uppercase()
        }
    }
    @Component
    public class BeanTransformer implements MappingTransformer<String, String> {
        @Nullable
        @Override
        public String transform(@NonNull MappingTransformerContext<? extends String> context) {
            return context.getOriginalValue() != null ? context.getOriginalValue().toUpperCase() : null;
        }
    }
    @Configuration
    class MyConfiguration {
        @Bean
        fun beanTransformer(): BeanTransformer {
            return BeanTransformer()
        }
    }
    @Configuration
    public class MyConfiguration {
        @Bean
        public BeanTransformer beanTransformer() {
            return new BeanTransformer();
        }
    }
    @Component
    class BeanDecorator: MappingDecorator<MyPojo, MyPojoDisplay> {
        override fun decorate(context: MappingDecoratorContext<MyPojo, MyPojoDisplay>) {
            // decorate
        }
    }
    @Component
    public class BeanDecorator implements MappingDecorator<MyPojo, MyPojoDisplay> {
        @Override
        public void decorate(@NonNull MappingDecoratorContext<MyPojo, MyPojoDisplay> context) {
            // decorate
        }
    }
    @Configuration
    class MyConfiguration {
        @Bean
        fun beanDecorator(): BeanDecorator {
            return BeanDecorator()
        }
    }
    @Configuration
    public class MyConfiguration {
        @Bean
        public BeanDecorator beanDecorator() {
            return new BeanDecorator();
        }
    }
    @Configuration
    class MyCustomizer : ShapeShiftBuilderCustomizer {
        override fun customize(builder: ShapeShiftBuilder) {
            builder.withDefaultMappingStrategy(MappingStrategy.MAP_ALL)
        }
    }
    @Configuration
    public class MyCustomizer implements ShapeShiftBuilderCustomizer {
        @Override
        public void customize(ShapeShiftBuilder builder) {
            builder.withDefaultMappingStrategy(MappingStrategy.MAP_ALL);
        }
    }
    data class SimpleEntity(
        val name: String,
        val description: String,
        val privateData: String
    )
    data class SimpleEntityDisplay(
        val name: String = "",
        val description: String = ""
    )
    val mapper = mapper<SimpleEntity, SimpleEntityDisplay> {
        SimpleEntity::name mappedTo SimpleEntityDisplay::name
        SimpleEntity::description mappedTo SimpleEntityDisplay::description
    }
    val shapeShift = ShapeShiftBuilder()
        .withMapping(mapper)
        .build()
    val simpleEntity = SimpleEntity("test", "test description", "private data")
    val simpleEntityDisplay = shapeShift.map<SimpleEntityDisplay>(simpleEntity)
    class From {
        var child: Child = Child()
        
        class Child {
            var value: String?
        }
    }
    
    class To {
        var childValue: String?
    }
    val mapper = mapper<From, To> {
        From::child..From.Child::value mappedTo To::childValue
    }
    class From {
        var grandChildValue: String?
    }
    
    class To {
        var child: Child = Child()
        
        class Child {
            var grandChild: GrandChild = GrandChild()
        }
        
        class GrandChild {
            var value: String?
        }
    }
    val mapper = mapper<From, To> {
        From::grandChildValue mappedTo To::child..To.Child::grandChild..To.GrandChild::value
    }
    data class SimpleEntity(
        val commaDelimitedString: String
    )
    data class SimpleEntityDisplay(
        val stringList: List<String> = emptyList()
    )
    class StringToListMappingTransformer : MappingTransformer<String, List<String>> {
        override fun transform(context: MappingTransformerContext<out String>): List<String>? {
            return context.originalValue?.split(",")
        }
    }
    val mapper = mapper<SimpleEntity, SimpleEntityDisplay> {
        SimpleEntity::commaDelimitedString mappedTo SimpleEntityDisplay::stringList withTransformer StringToListMappingTransformer::class
    }
    val mapper = mapper<SimpleEntity, SimpleEntityDisplay> {
        SimpleEntity::commaDelimitedString mappedTo SimpleEntityDisplay::stringList withTransformer {
            it.originalValue?.split(",")
        }
    }
    val mapper = mapper<SimpleEntity, SimpleEntityDisplay> {
        autoMap(AutoMappingStrategy.BY_NAME)
        SimpleEntity::name mappedTo SimpleEntityDisplay::fullName
    }
    data class SimpleEntity(
        val name: String
    )
    data class SimpleEntityDisplay(
        val name: String = ""
    )
    class NotBlankStringCondition : MappingCondition<String> {
        override fun isValid(context: MappingConditionContext<String>): Boolean {
            return !context.originalValue.isNullOrBlank()
        }
    }
    val mapper = mapper<SimpleEntity, SimpleEntityDisplay> {
        SimpleEntity::name mappedTo SimpleEntityDisplay::name withCondition NotBlankStringCondition::class
    }
    val mapper = mapper<SimpleEntity, SimpleEntityDisplay> {
        SimpleEntity::name mappedTo SimpleEntityDisplay::name withCondition {
            !it.originalValue.isNullOrBlank()
        }
    }
    data class User(
        var firstName: String,
        var lastName: String
    )
    data class UserDisplay(
        var firstName: String,
        var lastName: String,
        var fullName: String
    )
    class UserUserDisplayDecorator : MappingDecorator<User, UserDisplay> {
        override fun decorate(context: MappingDecoratorContext<User, UserDisplay>) {
            val (from, to) = context
            to.fullName = "${from.firstName} ${from.lastName}"
        }
    }
    val shapeShift = ShapeShiftBuilder()
        .withMapping<User, UserDisplay> {
            User::firstName mappedTo UserDisplay::firstName
            User::lastName mappedTo UserDisplay::lastName
            decorate(UserUserDisplayDecorator())
        }
        .build()
    val shapeShift = ShapeShiftBuilder()
        .withMapping<User, UserDisplay> {
            User::firstName mappedTo UserDisplay::firstName
            User::lastName mappedTo UserDisplay::lastName
            decorate {
                val (from, to) = it
                to.fullName = "${from.firstName} ${from.lastName}"
            }
        }
        .build()
    val mapper = mapper<SimpleEntity, SimpleEntityDisplay> {
        SimpleEntity::name mappedTo SimpleEntityDisplay::name overrideStrategy MappingStrategy.MAP_ALL
    }
    Creating Conditions

    To create a condition, create a new class implementing the MappingCondition<T> interface.

    class NotBlankStringCondition : MappingCondition<String> {
        override fun isValid(context: MappingConditionContext<String>): Boolean {
            return !context.originalValue.isNullOrBlank()
        }
    }
    public class NotBlankStringCondition implements MappingCondition<String> {
        @Override
        public boolean isValid(@NonNull MappingConditionContext<String> context) {
            return context.getOriginalValue
    

    The condition above checks that a string is not null or blank. After creating the condition class, all that is left is to use the condition.

    Adding Conditions

    Adding conditions to fields is available in both DSL and annotation mapping. Conditions can be added only to fields with the same type as the condition.

    Annotations

    The condition can be added to annotation mapping using the condition attribute.

    We mapped the name field and added the condition. Now the name field will be mapped to SimpleEntityDisplay only if its value is not null or blank.

    Kotlin DSL

    The condition can be added to field mapping using the withCondition function.

    Using the DSL, conditions can also be added inline.

    Again, the name field will be mapped to SimpleEntityDisplay only if its value is not null or blank.

    Java Builder

    The condition can be added to field mapping builder using the withCondition function.

    Also with the builder, conditions can also be added inline.

    data class SimpleEntity(
        val name: String
    )
    public class SimpleEntity {
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    data class SimpleEntityDisplay(
        val name: String = ""
    )
    public class SimpleEntityDisplay {
        private String name = "";
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    and
    lastName
    fields to the
    fullName
    field.

    Adding Decorators

    Adding decorators is available through the ShapeShiftBuilder class. Decorators can be added inline or as a separate class.

    Class Decorators

    To create a decorator class implement the MappingDecorator interface.

    class UserUserDisplayDecorator : MappingDecorator<User, UserDisplay> {
        override fun decorate(context: MappingDecoratorContext<User, UserDisplay>) {
            val (from, to) = context
            to.fullName = "${
    
    public class UserUserDisplayDecorator implements MappingDecorator<User, UserDisplay> {
        @Override
        public void decorate(@NonNull MappingDecoratorContext<User, UserDisplay> mappingDecoratorContext) {
    
    

    And register it to the ShapeShift instance.

    Inline Decorators

    It is also possible to add the decorator logic inline.

    data class User(
        var firstName: String,
        var lastName: String
    )
    public class User {
        private String firstName;
        private String lastName;
    
        public String getFirstName() {
            return firstName;
        }
    
        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }
    
        public String getLastName() {
            return lastName;
        }
    
        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
    }
    data class UserDisplay(
        var firstName: String,
        var lastName: String,
        var fullName: String
    )
    public class UserDisplay {
        private String firstName;
        private String lastName;
        private String fullName;
    
        public String getFirstName() {
            return firstName;
        }
    
        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }
    
        public String getLastName() {
            return lastName;
        }
    
        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
    
        public String getFullName() {
            return fullName;
        }
    
        public void setFullName(String fullName) {
            this.fullName = fullName;
        }
    }

    Quick Start

    In this quick start guide, we'll review the simplest use-case for ShapeShift, a simple mapping between two classes.

    Before getting started, make sure you have followed the installation steps outlined in the Installation guide.

    Classes

    We start by defining two classes, our source class SimpleEntity and our destination class SimpleEntityDisplay.

    Due to the fact that ShapeShift uses reflection behind the scenes, destination classes should have a no arg constructor. Alternatively, you can also pass already-instantiated destination objects to the map method.

    Annotations

    If you don't want to add annotations to your classes go to the or documentation to learn how to use external mapping.

    We can now start adding our annotations to the SimpleEntity class. In this example, we want to map the name and description fields to the name and description fields of the SimpleEntityDisplay class, but not the privateData field.

    To achieve this, we will use the @MappedField annotation on both of these fields. Additionally, we will define @DefaultMappingTarget on the SimpleEntity class, which will indicate that all fields annotated with @MappedField that do not specify a target should be mapped to the SimpleEntityDisplay class.

    Convert

    To instantiate ShapeShift we use the ShapeShiftBuilder.

    All that's left is to map the SimpleEntity instance to the SimpleEntityDisplay class.

    Test

    Now let's write a simple test to check this scenario.

    Additionally, we can also pass a destination instance to the map method, let's write a test to check this scenario as well.

    Full Example

    You can check out the full example .

    Next Steps

    We hope this quick start guide has given you a glimpse of the simplicity and power of ShapeShift. There's much more to learn, and we encourage you to keep reading about all of the different options available by reading their respective API documentation.

    Auto Mapping

    Auto map fields without any boiler-plate code.

    Auto mapping is used to reduce the amount of boiler-place code required to configure mapping between two classes.

    We start with our two classes, our source class SimpleEntity and our destination class SimpleEntityDisplay.

    data class SimpleEntity(
        val id: String,
        val name: String,
        val birthDate: Date,
        val email: String,
        val telephone: String
    
    public class SimpleEntity {
        private String id;
        private String name;
        private Date birthDate;
        private String email;
        private String telephone;
    
    data class SimpleEntityDisplay(
        val id: String = "",
        val fullName: String = "",
        val birthDate: Long = 0,
    
    
    public class SimpleEntityDisplay {
        private String id = "";
        private String name = "";
        private long birthDate = 0;
    
    

    Auto Mapping Strategy

    Auto mapping supports 3 different strategies:

    BY_NAME_AND_TYPE - When a field with the same name and type is available on both the source and destination classes. The field will be mapped automatically.

    In our example the fields that will be mapped in this strategy are: id, email and telephone.

    BY_NAME - When a field with the same name is available on both the source and destination classes, not necessarily of the same type. The field will be mapped automatically using .

    When using the BY_NAME strategy all fields with the same name and different types must have registered default transformers for the corresponding types. Otherwise, a runtime exception will be thrown.

    In our example the fields that will be mapped in this strategy are: id, email, telephone and birthDate. Note that a default transformer between Date and Long must be registered or a runtime exception will be thrown.

    NONE - The default strategy. Disables auto mapping completely. All mappings should be added manually.

    When a field is mapped manually, either in the source or destination classes, all auto mappings of that field are disabled.

    Configuring Auto Mapping

    Auto mapping is available in both DSL and annotation mapping.

    Annotations

    Auto mapping can be added using the @AutoMapping annotation.

    Two things to note:

    1. The name field is mapped manually because it has a different name in the target class.

    2. The @AutoMapping annotation has two attributes:

      1. target - If no target is added then the auto mapping will be configured to any target class. It is possible to add multiple

    Kotlin DSL

    Auto mapping can be added using the autoMap function.

    autoMap function receives the desired auto mapping strategy. It is possible to add any manual mapping to add/change mapping behavior.

    Java Builder

    Similar to the Kotlin DSL, auto mapping can be added using the autoMap function.

    With the builder, it is also possible to add additional manual mappings to add/change mapping behavior.

    Advanced Mapping

    Nested fields and multiple targets annotations mapping.

    In this guide, we'll review mapping nested fields, as well as defining mappings to multiple targets.

    Classes

    We start by defining our source and destination classes, and additionally a child class which will be used by both.

    Starting with the child class:

    data class AdvancedChildEntity(
        val childName: String
    )
    public class AdvancedChildEntity {
        private String childName;
    
        public String getChildName() {
            return childName;
        }
    
        public void setChildName
    

    Followed by our source class:

    In this example, we will have two separate destination classes, AdvancedEntityDisplay and ReducedAdvancedEntityDisplay.

    Annotations

    Let's start adding annotations to the source class. We will define a @DefaultMappingTarget annotation on the AdvancedEntity class, which will indicate that all fields annotated with @MappedField that do not specify a target should be mapped to the AdvancedEntityDisplay class. Seeing as we have two destinations in this example, we will have to define some of the mapping targets manually.

    We'll start by defining a simple @MappedField on the name field for both targets. We will also define a @MappedField annotation on the firstChild field, which will indicate that it should be mapped to the firstChildName field on the AdvancedEntityDisplay class, and repeat the same annotation for secondChild. To achieve this we will use the mapFrom and mapTo parameters. Notice that by doing this, we have extracted the name field from the AdvancedEntity class, and mapped it directly to the *ChildName

    Test

    Finally, let's write two tests to verify that our mapping is working correctly for both targets

    Full Example

    You can check out the full example .

    Implicit Transformers

    Useful transformers for subclasses mapping.

    In this guide, we'll review two new built in transformers added in ShapeShift version 0.0.7, ImplicitMappingTransformer and ImplicitCollectionMappingTransformer.

    ImplicitMappingTransformer

    ImplicitMappingTransformer is used to transform subclasses mapped with ShapeShift. Lets look at the following example:

    We have mapped Address

    @DefaultMappingTarget(SimpleEntityDisplay::class)
    data class SimpleEntity(
        @MappedField(condition = NotBlankStringCondition::class)
        val name: String,
    )
    @DefaultMappingTarget(SimpleEntityDisplay.class)
    public class SimpleEntity {
        @MappedField(condition = NotBlankStringCondition.class)
        private String name;
        // Getters and Setters...
    }
    val mapper = mapper<SimpleEntity, SimpleEntityDisplay> {
        SimpleEntity::name mappedTo SimpleEntityDisplay::name withCondition NotBlankStringCondition::class
    }
    val mapper = mapper<SimpleEntity, SimpleEntityDisplay> {
        SimpleEntity::name mappedTo SimpleEntityDisplay::name withCondition {
            !it.originalValue.isNullOrBlank()
        }
    }
    MappingDefinition mappingDefinition = new MappingDefinitionBuilder(SimpleEntity.class, SimpleEntityDisplay.class)
            .mapField("name", "name")
            .withCondition(NotBlankStringCondition.class)
            .build();
    MappingDefinition mappingDefinition = new MappingDefinitionBuilder(SimpleEntity.class, SimpleEntityDisplay.class)
            .mapField("name", "name")
            .withCondition(context -> context.getOriginalValue() != null && !((String) context.getOriginalValue()).trim().isEmpty())
            .build();
    val shapeShift = ShapeShiftBuilder()
        .withDecorator(UserUserDisplayDecorator())
        .build()
    ShapeShift shapeShift = new ShapeShiftBuilder()
            .withDecorator(User.class, UserDisplay.class, new UserUserDisplayDecorator())
            .build();
    val shapeShift = ShapeShiftBuilder()
        .withDecorator(MappingDecorator<User, UserDisplay> {
            val (from, to) = it
            to.fullName = "${from.firstName} ${from.lastName}"
        })
        .build()
    ShapeShift shapeShift = new ShapeShiftBuilder()
            .withDecorator(User.class, UserDisplay.class, mappingDecoratorContext -> {
                User from = mappingDecoratorContext.getFrom();
                UserDisplay to = mappingDecoratorContext.getTo();
                to.setFullName(from.getFirstName() + " " + from.getLastName());
            })
            .build();
    ()
    !=
    null
    &&
    !
    context
    .
    getOriginalValue
    ()
    .
    trim
    ()
    .
    isEmpty
    ();
    }
    }
    from.firstName
    }
    ${
    from.lastName
    }
    "
    }
    }
    User from = mappingDecoratorContext.getFrom();
    UserDisplay to = mappingDecoratorContext.getTo();
    to.setFullName(from.getFirstName() + " " + from.getLastName());
    }
    }
    Kotlin DSL
    Java Builder
    here
    data class SimpleEntity(
        val name: String,
        val description: String,
        val privateData: String
    )
    public class SimpleEntity {
        private String name;
        private String description;
        private String privateData;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getDescription() {
            return description;
        }
    
        public void setDescription(String description) {
            this.description = description;
        }
    
        public String getPrivateData() {
            return privateData;
        }
    
        public void setPrivateData(String privateData) {
            this.privateData = privateData;
        }
    }
    data class SimpleEntityDisplay(
        val name: String = "",
        val description: String = ""
    )
    public class SimpleEntityDisplay {
        private String name = "";
        private String description = "";
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getDescription() {
            return description;
        }
    
        public void setDescription(String description) {
            this.description = description;
        }
    }
    @AutoMapping
    annotation for multiple target classes.
  • strategy - The auto mapping strategy. Default value NONE.

  • )
    public String getId() {
    return id;
    }
    public void setId(String id) {
    this.id = id;
    }
    public String getName() {
    return name;
    }
    public void setName(String name) {
    this.name = name;
    }
    public Date getBirthDate() {
    return birthDate;
    }
    public void setBirthDate(Date birthDate) {
    this.birthDate = birthDate;
    }
    public String getEmail() {
    return email;
    }
    public void setEmail(String email) {
    this.email = email;
    }
    public String getTelephone() {
    return telephone;
    }
    public void setTelephone(String telephone) {
    this.telephone = telephone;
    }
    }
    val email: String = "",
    val telephone: String = ""
    )
    private String email = "";
    private String telephone = "";
    public String getId() {
    return id;
    }
    public void setId(String id) {
    this.id = id;
    }
    public String getName() {
    return name;
    }
    public void setName(String name) {
    this.name = name;
    }
    public long getBirthDate() {
    return birthDate;
    }
    public void setBirthDate(long birthDate) {
    this.birthDate = birthDate;
    }
    public String getEmail() {
    return email;
    }
    public void setEmail(String email) {
    this.email = email;
    }
    public String getTelephone() {
    return telephone;
    }
    public void setTelephone(String telephone) {
    this.telephone = telephone;
    }
    }
    default transformers
    field on the
    AdvancedEntityDisplay
    class. The
    ReducedAdvancedEntityDisplay
    class will have the same mapping as the
    AdvancedEntityDisplay
    class, but will omit the
    secondChildName
    field;
    (
    String
    childName) {
    this.childName = childName;
    }
    }
    here
    data class AdvancedEntity(
        val name: String,
        val firstChild: AdvancedChildEntity,
        val secondChild: AdvancedChildEntity
    )
    public class AdvancedEntity {
        private String name;
        private AdvancedChildEntity firstChild;
        private AdvancedChildEntity secondChild;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public AdvancedChildEntity getFirstChild() {
            return firstChild;
        }
    
        public void setFirstChild(AdvancedChildEntity firstChild) {
            this.firstChild = firstChild;
        }
    
        public AdvancedChildEntity getSecondChild() {
            return secondChild;
        }
    
        public void setSecondChild(AdvancedChildEntity secondChild) {
            this.secondChild = secondChild;
        }
    }
    data class AdvancedEntityDisplay(
        val name: String = "",
        val firstChildName: String = "",
        val secondChildName: String = ""
    )
    public class AdvancedEntityDisplay {
        private String name = "";
        private String firstChildName = "";
        private String secondChildName = "";
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getFirstChildName() {
            return firstChildName;
        }
    
        public void setFirstChildName(String firstChildName) {
            this.firstChildName = firstChildName;
        }
    
        public String getSecondChildName() {
            return secondChildName;
        }
    
        public void setSecondChildName(String secondChildName) {
            this.secondChildName = secondChildName;
        }
    }
    @DefaultMappingTarget(SimpleEntityDisplay::class)
    data class SimpleEntity(
        @MappedField
        val name: String,
        @MappedField
        val description: String,
        val privateData: String
    )
    @DefaultMappingTarget(SimpleEntityDisplay.class)
    public class SimpleEntity {
        @MappedField
        private String name;
        @MappedField
        private String description;
        private String privateData;
        // Getters and Setters...
    }
    val shapeShift = ShapeShiftBuilder().build()
    ShapeShift shapeShift = new ShapeShiftBuilder().build();
    val shapeShift = ShapeShiftBuilder().build()
    val simpleEntity = SimpleEntity("test", "test description", "private data")
    val simpleEntityDisplay = shapeShift.map<SimpleEntityDisplay>(simpleEntity)
    ShapeShift shapeShift = new ShapeShiftBuilder().build();
    SimpleEntity simpleEntity = new SimpleEntity();
    simpleEntity.setName("test");
    simpleEntity.setDescription("test description");
    simpleEntity.setPrivateData("private data");
    SimpleEntityDisplay simpleEntityDisplay = shapeShift.map(simpleEntity, SimpleEntityDisplay.class);
    @Test
    internal fun `test simple mapping`() {
        val shapeShift = ShapeShiftBuilder().build()
        val simpleEntity = SimpleEntity("test", "test description", "private data")
        val result = shapeShift.map<SimpleEntityDisplay>(simpleEntity)
        expectThat(result.name)
            .isEqualTo("test")
        expectThat(result.description)
            .isEqualTo("test description")
    }
    @Test
    public void testSimpleMapping() {
        ShapeShift shapeShift = new ShapeShiftBuilder().build();
        SimpleEntity simpleEntity = new SimpleEntity();
        simpleEntity.setName("test");
        simpleEntity.setDescription("test description");
        simpleEntity.setPrivateData("private data");
        SimpleEntityDisplay simpleEntityDisplay = shapeShift.map(simpleEntity, SimpleEntityDisplay.class);
        assertEquals(simpleEntityDisplay.getName(), "test");
        assertEquals(simpleEntityDisplay.getDescription(), "test description");
    }
    @Test
    internal fun `test simple mapping with premade destination instance`() {
        val shapeShift = ShapeShiftBuilder().build()
        val simpleEntity = SimpleEntity("test", "test description", "private data")
        val result = shapeShift.map(simpleEntity, SimpleEntityDisplay())
        expectThat(result.name)
            .isEqualTo("test")
        expectThat(result.description)
            .isEqualTo("test description")
    }
    @Test
    public void testSimpleMappingWithPremadeDestinationInstance() {
        ShapeShift shapeShift = new ShapeShiftBuilder().build();
        SimpleEntity simpleEntity = new SimpleEntity();
        simpleEntity.setName("test");
        simpleEntity.setDescription("test description");
        simpleEntity.setPrivateData("private data");
        SimpleEntityDisplay simpleEntityDisplay = shapeShift.map(simpleEntity, new SimpleEntityDisplay());
        assertEquals(simpleEntityDisplay.getName(), "test");
        assertEquals(simpleEntityDisplay.getDescription(), "test description");
    }
    @AutoMapping(SimpleEntityDisplay::class, strategy = AutoMappingStrategy.BY_NAME)
    @DefaultMappingTarget(SimpleEntityDisplay::class)
    data class SimpleEntity(
        val id: String,
        @MappedField(mapTo = "fullName")
        val name: String,
        val birthDate: Date,
        val email: String,
        val telephone: String
    )
    @AutoMapping(target = SimpleEntityDisplay.class, strategy = AutoMappingStrategy.BY_NAME)
    @DefaultMappingTarget(SimpleEntityDisplay.class)
    public class SimpleEntity {
        private String id;
        @MappedField(mapTo = "fullName")
        private String name;
        private Date birthDate;
        private String email;
        private String telephone;
        // Getters and Setters...
    }
    val mapper = mapper<SimpleEntity, SimpleEntityDisplay> {
        autoMap(AutoMappingStrategy.BY_NAME)
        SimpleEntity::name mappedTo SimpleEntityDisplay::fullName
    }
    MappingDefinition mappingDefinition = new MappingDefinitionBuilder(SimpleEntity.class, SimpleEntityDisplay.class)
            .autoMap(AutoMappingStrategy.BY_NAME)
            .mapField("name", "fullName")
            .build();
    data class ReducedAdvancedEntityDisplay(
        val name: String = "",
        val firstChildName: String = ""
    )
    public class ReducedAdvancedEntityDisplay {
        private String name = "";
        private String firstChildName = "";
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getFirstChildName() {
            return firstChildName;
        }
    
        public void setFirstChildName(String firstChildName) {
            this.firstChildName = firstChildName;
        }
    }
    
    @DefaultMappingTarget(AdvancedEntityDisplay::class)
    data class AdvancedEntity(
        @MappedField
        @MappedField(target = ReducedAdvancedEntityDisplay::class)
        val name: String,
    
        @MappedField(mapFrom = "childName", mapTo = "firstChildName")
        @MappedField(target = ReducedAdvancedEntityDisplay::class, mapFrom = "childName", mapTo = "firstChildName")
        val firstChild: AdvancedChildEntity,
    
        @MappedField(mapFrom = "childName", mapTo = "secondChildName")
        val secondChild: AdvancedChildEntity
    )
    @DefaultMappingTarget(AdvancedEntityDisplay.class)
    public class AdvancedEntity {
        @MappedField
        @MappedField(target = ReducedAdvancedEntityDisplay.class)
        private String name;
        
        @MappedField(mapFrom = "childName", mapTo = "firstChildName")
        @MappedField(target = ReducedAdvancedEntityDisplay.class, mapFrom = "childName", mapTo = "firstChildName")
        private AdvancedChildEntity firstChild;
        
        @MappedField(mapFrom = "childName", mapTo = "secondChildName")
        private AdvancedChildEntity secondChild;
        // Getters and Setters...
    }
    @Test
    fun `test advanced mapping for AdvancedEntityDisplay`() {
        val shapeShift = ShapeShift()
        val advancedEntity = AdvancedEntity(
                "test",
                AdvancedChildEntity("first child"),
                AdvancedChildEntity("second child")
        )
        val result = shapeShift.map<AdvancedEntityDisplay>(advancedEntity)
        expectThat(result.name)
                .isEqualTo("test")
        expectThat(result.firstChildName)
                .isEqualTo("first child")
        expectThat(result.secondChildName)
                .isEqualTo("second child")
    }
    
    @Test
    fun `test advanced mapping for ReducedAdvancedEntityDisplay`() {
        val shapeShift = ShapeShift()
        val advancedEntity = AdvancedEntity(
                "test",
                AdvancedChildEntity("first child"),
                AdvancedChildEntity("second child")
        )
        val result = shapeShift.map<ReducedAdvancedEntityDisplay>(advancedEntity)
        expectThat(result.name)
                .isEqualTo("test")
        expectThat(result.firstChildName)
                .isEqualTo("first child")
    }
    @Test
    public void testAdvancedMappingForAdvancedEntityDisplay() {
        ShapeShift shapeShift = new ShapeShiftBuilder().build();
        AdvancedEntity advancedEntity = new AdvancedEntity();
        advancedEntity.setName("test");
    
        AdvancedChildEntity firstChild = new AdvancedChildEntity();
        firstChild.setChildName("first child");
        advancedEntity.setFirstChild(firstChild);
    
        AdvancedChildEntity secondChild = new AdvancedChildEntity();
        secondChild.setChildName("second child");
        advancedEntity.setSecondChild(secondChild);
    
        AdvancedEntityDisplay result = shapeShift.map(advancedEntity, AdvancedEntityDisplay.class);
        assertEquals(result.getName(), "test");
        assertEquals(result.getFirstChildName(), "first child");
        assertEquals(result.getSecondChildName(), "second child");
    }
    
    @Test
    public void testAdvancedMappingForReducedAdvancedEntityDisplay() {
        ShapeShift shapeShift = new ShapeShiftBuilder().build();
        AdvancedEntity advancedEntity = new AdvancedEntity();
        advancedEntity.setName("test");
    
        AdvancedChildEntity firstChild = new AdvancedChildEntity();
        firstChild.setChildName("first child");
        advancedEntity.setFirstChild(firstChild);
    
        AdvancedChildEntity secondChild = new AdvancedChildEntity();
        secondChild.setChildName("second child");
        advancedEntity.setSecondChild(secondChild);
    
        ReducedAdvancedEntityDisplay result = shapeShift.map(advancedEntity, ReducedAdvancedEntityDisplay.class);
        assertEquals(result.getName(), "test");
        assertEquals(result.getFirstChildName(), "first child");
    }
    class to its display class
    AddressDisplay
    .
    @DefaultMappingTarget(AddressDisplay::class)
    class Address {
        @MappedField
        var country: String? = null
    
        @MappedField
        var city: String? = null
    
    @DefaultMappingTarget(AddressDisplay::class)
    public class Address {
        @MappedField
        private String country;
    
        @MappedField
        private String city;
    
        @
    

    We can use ShapeShift to map Address to AddressDisplay. But what we do if Address is a subclass? We use the ImplicitMappingTransformer.

    The ImplicitMappingTransformer uses the ShapeShift instance internally to map Address to AddressDisplay.

    ImplicitCollectionMappingTransformer

    ImplicitCollectionMappingTransformer has the same job as ImplicitMappingTransformer but for collections.

    Java Builder

    Java builder based usage of the ShapeShift library.

    Basic Mapping

    We start by defining two classes, our source class SimpleEntity and our destination class SimpleEntityDisplay.

    We can now create a simple mapper. In this example, we want to map the name and description fields of SimpleEntity to the name and description fields of the SimpleEntityDisplay class, but not the privateData field.

    To instantiate ShapeShift we use the ShapeShiftBuilder and register our mapper:

    All that's left is to map the SimpleEntity instance to the SimpleEntityDisplay class.

    Mapping Fields

    In the example above we did basic mapping between fields. But what if we want to map from/to deep fields of child classes?

    In order to access child classes we can use the full path of a field. Let's look at the following example:

    We want to map the value field in Child class inside the From class to the childValue field in the To class. We will use the full path of value which is child.value.

    The full path is supported in both source and destination fields, it also supports multi level depth (e.g. x.y.z).

    Transformers

    Field transformers are a way to transform a field from one type to another when mapping it to a destination class. More about the ins-and-outs of transformers is available here:

    The withTransformer function has 2 options to use transformers. Let's look at the following classes.

    We want to map the commaDelimitedString field to the stringList field and change the field type from String to List<String> while doing so. To accomplish that we will use a transformer.

    Class Transformer

    Our first option is to create a transformer class, StringToListMappingTransformer;

    All we need to do to use our transformer is to pass it to the withTransformer function.

    Transformers must be registered to the ShapeShift instance in order to be used. More info about registering transformers is available in the .

    Inline Transformer

    Our second option is to use an inline transformer. When we don't need to reuse a transformer we can just add its logic to the builder.

    Auto Mapping

    Auto mapping is used to reduce the amount of boiler-place code required to configure mapping between two classes. More info about auto mapping is available here:

    Auto mapping can be added using the autoMap function.

    autoMap function receives the desired . It is possible to add any manual mapping to add/change mapping behavior.

    Mapping Condition

    Conditions are used to determine wether a field should be mapped according to certain logic. More info about conditions is available here:

    Let's look at the following classes.

    We want to map the name field only if it's not null or blank. The withCondition function has 2 options to add conditions.

    Class Condition

    Our first option is to create a condition class. The condition receives context with the original value of the field and checks that it is not null or blank.

    We will create our mapper and add the condition.

    Inline Condition

    Our second option is to use an inline condition. When we don't need to reuse a condition we can just add its logic to the builder.

    Decorators

    Decorators allow to add additional logic to the mapping operation. More info about conditions is available here:

    Let's look at the following classes.

    We want to merge the firstName and lastName fields to the fullName field in addition to mapping them to their respectable fields.

    Decorators can be added inline or as a separate class.

    Class Decorators

    To create a decorator class implement the MappingDecorator interface.

    And register it to the ShapeShift instance.

    Inline Decorators

    It is also possible to add the decorator logic inline.

    Object Suppliers

    Due to the fact that ShapeShift uses reflection behind the scenes, destination classes need a no arg constructor. But in some cases you have no control over the destination classes and cannot modify them to add a no arg constructor. This is where Object Suppliers comes into play, you can register object suppliers to the ShapeShift instance to add your own logic for instance generation.

    More info about Object Suppliers is available here:

    Override Mapping Strategy

    The overrideStrategy function allows you to override the default mapping strategy configured on the ShapeShift instance.

    More info about mapping strategy is available here:

    Transformers

    Transform field type/value between the source and target classes.

    In this guide we will see how we can use transformers to map fields. Field transformers are a way to transform a field from one type to another when mapping it to a destination class. For example, you might want to map a field from a String to a List<String> where the source field is comma delimited. In our example, we will explore this use case, as well as an implicit (default) transformation from Date to Long (milliseconds).

    Classes

    Like before, we start by defining our source and destination classes:

    Creating Transformer

    Let's first create our custom StringToListMappingTransformer;

    The MappingTransformerContext holds all the required data to perform simple and complex transformations. In this example, all we need to do is to take the original value and split it.

    Registering Transformers

    Since we're using custom transformers, we will have to instantiate ShapeShift using ShapeShiftBuilder and define our two transformers. In ShapeShift, you define a transformer by providing a TransformerRegistration object.

    The registration object is used to define the type of the transformer, its instance, and whether it's a default transformer. We will register the DateToLongTransformer as a , and the StringToListTransformer as a normal transformer.

    Default Transformers

    When you can indicate wether a transformer is a default transformer. A default transformer of types <A, B> is used when you map a field of type <A> to field of type <B> without specifying a transformer to be used.

    ShapeShift comes out of the box with some default transformers. The default transformers are available in the dev.krud.shapeshift.transformer package .

    Examples for default transformers: AnyToStringMappingTransformer, DateToLongMappingTransformer.

    Exclude Default Transformers

    To exclude the out of the box default transformers just call the excludeDefaultTransformers when creating the ShapeShift instance.

    Using Transformers

    Annotations

    We can now add our annotations;

    Note that we did not need to specify a transformer on creationDate since the DateToLongTransformer is a default transformer for the Date type with a Long destination type.

    Kotlin DSL

    We can create the same mapping with the DSL using the withTransformer function.

    The DSL also supports inline transformer. When we don't need to reuse a transformer we can just add its logic to the DSL.

    Java Builder

    We can create the same mapping with the builder using the withTransformer function.

    As the DSL, the builder also supports inline transformer.

    Test

    Let's write a test to verify that our mapping is correct;

    Full Example

    You can check out the full example .

    @DefaultMappingTarget(UserDisplay::class)
    class User {
        @MappedField
        var name: String? = null
    
        @MappedField(transformer = ImplicitMappingTransformer::class)
        var address: Address? = null
    }
    
    class UserDisplay {
        var name: String? = null
        var address: AddressDisplay? = null
    }
    @DefaultMappingTarget(UserDisplay::class)
    public class User {
        @MappedField
        private String name;
    
        @MappedField(transformer = ImplicitMappingTransformer.class)
        private Address address;
    
        // Getters and Setters...
    }
    
    public class UserDisplay {
        private String name;
        private AddressDisplay address;
    
        // Getters and Setters...
    }
    @DefaultMappingTarget(UserDisplay::class)
    class User {
        @MappedField
        var name: String? = null
    
        @MappedField(transformer = ImplicitCollectionMappingTransformer::class)
        var addresses: List<Address>? = null
    }
    
    class UserDisplay {
        var name: String? = null
        var addresses: List<AddressDisplay>? = null
    }
    @DefaultMappingTarget(UserDisplay::class)
    public class User {
        @MappedField
        private String name;
    
        @MappedField(transformer = ImplicitCollectionMappingTransformer.class)
        private List<Address> addresses;
    
        // Getters and Setters...
    }
    
    public class UserDisplay {
        private String name;
        private List<AddressDisplay> addresses;
    
        // Getters and Setters...
    }
    public class SimpleEntity {
        private String name;
        private String description;
        private String privateData;
    
        public SimpleEntity() {
        }
    
        public SimpleEntity(String name, String description, String privateData) {
            this.name = name;
            this.description = description;
            this.privateData = privateData;
        }
    
        // Getters and Setters...
    }
    public class SimpleEntityDisplay {
        private String name;
        private String description;
    
        public SimpleEntityDisplay() {
        }
    
        // Getters and Setters...
    }
    @MappedField
    var address: String? = null
    }
    class AddressDisplay {
    var country: String? = null
    var city: String? = null
    var address: String? = null
    }
    MappedField
    private String address;
    // Getters and Setters...
    }
    public class AddressDisplay {
    private String country;
    private String city;
    private String address;
    // Getters and Setters...
    }
    Transformers
    transformers page
    Transformers
    Auto Mapping
    auto mapping strategy
    Conditions
    Decorators
    Object Suppliers
    Mapping Strategy
    default transformer
    registering transformers
    here
    here
    data class SimpleEntity(
        val creationDate: Date,
        val commaDelimitedString: String, 
    )
    public class SimpleEntity {
        private Date creationDate;
        private String commaDelimitedString;
    
        public Date getCreationDate() {
            return creationDate;
        }
    
        public void setCreationDate(Date creationDate) {
            this.creationDate = creationDate;
        }
    
        public String getCommaDelimitedString() {
            return commaDelimitedString;
        }
    
        public void setCommaDelimitedString(String commaDelimitedString) {
            this.commaDelimitedString = commaDelimitedString;
        }
    }
    data class SimpleEntityDisplay(
        val creationDate: Long = 0,
        val stringList: List<String> = emptyList()
    )
    public class SimpleEntityDisplay {
        private long creationDate = 0;
        private List<String> stringList = new ArrayList<>();
    
        public long getCreationDate() {
            return creationDate;
        }
    
        public void setCreationDate(long creationDate) {
            this.creationDate = creationDate;
        }
    
        public List<String> getStringList() {
            return stringList;
        }
    
        public void setStringList(List<String> stringList) {
            this.stringList = stringList;
        }
    }
    class StringToListMappingTransformer : MappingTransformer<String, List<String>> {
        override fun transform(context: MappingTransformerContext<out String>): List<String>? {
            return context.originalValue?.split(",")
        }
    }
    public class StringToListMappingTransformer implements MappingTransformer<String, List<String>> {
        @Nullable
        @Override
        public List<String> transform(@NonNull MappingTransformerContext<? extends String> context) {
            return context.getOriginalValue() != null
                    ? Arrays.asList(context.getOriginalValue().split(","))
                    : null;
        }
    }
    MappingDefinition mappingDefinition = new MappingDefinitionBuilder(SimpleEntity.class, SimpleEntityDisplay.class)
            .mapField("name", "name")
            .mapField("description", "description")
            .build();
    ShapeShift shapeShift = new ShapeShiftBuilder()
            .withMapping(mappingDefinition)
            .build();
    SimpleEntity simpleEntity = new SimpleEntity("test", "test description", "private data");
    SimpleEntityDisplay simpleEntityDisplay = shapeShift.map(simpleEntity, SimpleEntityDisplay.class);
    public class From {
    
        private Child child = new Child();
        // Getters and Setters...
    
        class Child {
            private String value;
            // Getters and Setters...
        }
    }
    
    public class To {
        private String childValue;
        // Getters and Setters...
    }
    MappingDefinition mappingDefinition = new MappingDefinitionBuilder(From.class, To.class)
            .mapField("child.value", "childValue")
            .build();
    public class SimpleEntity {
        private String commaDelimitedString;
    
        public SimpleEntity() {
        }
    
        public SimpleEntity(String commaDelimitedString) {
            this.commaDelimitedString = commaDelimitedString;
        }
    
        // Getters and Setters...
    }
    public class SimpleEntityDisplay {
        private List<String> stringList;
    
        public SimpleEntityDisplay() {
        }
    
        // Getters and Setters...
    }
    
    public class StringToListMappingTransformer implements MappingTransformer<String, List<String>> {
        @Nullable
        @Override
        public List<String> transform(@NonNull MappingTransformerContext<? extends String> context) {
            return context.getOriginalValue() != null
                    ? Arrays.asList(context.getOriginalValue().split(","))
                    : null;
        }
    }
    MappingDefinition mappingDefinition = new MappingDefinitionBuilder(SimpleEntity.class, SimpleEntityDisplay.class)
            .mapField("commaDelimitedString", "stringList")
            .withTransformer(StringToListMappingTransformer.class)
            .build();
    MappingDefinition mappingDefinition = new MappingDefinitionBuilder(SimpleEntity.class, SimpleEntityDisplay.class)
            .mapField("commaDelimitedString", "stringList")
            .withTransformer(context -> context.getOriginalValue() != null 
                    ? Arrays.asList(((String) context.getOriginalValue()).split(",")) 
                    : null)
            .build();
    MappingDefinition mappingDefinition = new MappingDefinitionBuilder(SimpleEntity.class, SimpleEntityDisplay.class)
            .autoMap(AutoMappingStrategy.BY_NAME)
            .mapField("name", "fullName")
            .build();
    public class SimpleEntity {
        private String name;
    
        public SimpleEntity() {
        }
    
        // Getters and Setters...
    }
    public class SimpleEntityDisplay {
        private String name;
    
        public SimpleEntityDisplay() {
        }
    
        // Getters and Setters...
    }
    public class NotBlankStringCondition implements MappingCondition<String> {
        @Override
        public boolean isValid(@NonNull MappingConditionContext<String> context) {
            return context.getOriginalValue() != null && !context.getOriginalValue().trim().isEmpty();
        }
    }
    MappingDefinition mappingDefinition = new MappingDefinitionBuilder(SimpleEntity.class, SimpleEntityDisplay.class)
            .mapField("name", "name")
            .withCondition(NotBlankStringCondition.class)
            .build();
    MappingDefinition mappingDefinition = new MappingDefinitionBuilder(SimpleEntity.class, SimpleEntityDisplay.class)
            .mapField("name", "name")
            .withCondition(context -> context.getOriginalValue() != null && !((String) context.getOriginalValue()).trim().isEmpty())
            .build();
    public class User {
        private String firstName;
        private String lastName;
        // Getters and Setters...
    }
    public class UserDisplay {
        private String firstName;
        private String lastName;
        private String fullName;
        // Getters and Setters...
    }
    public class UserUserDisplayDecorator implements MappingDecorator<User, UserDisplay> {
        @Override
        public void decorate(@NonNull MappingDecoratorContext<User, UserDisplay> mappingDecoratorContext) {
            User from = mappingDecoratorContext.getFrom();
            UserDisplay to = mappingDecoratorContext.getTo();
            to.setFullName(from.getFirstName() + " " + from.getLastName());
        }
    }a
    ShapeShift shapeShift = new ShapeShiftBuilder()
            .withDecorator(User.class, UserDisplay.class, new UserUserDisplayDecorator())
            .build();
    ShapeShift shapeShift = new ShapeShiftBuilder()
            .withDecorator(User.class, UserDisplay.class, mappingDecoratorContext -> {
                User from = mappingDecoratorContext.getFrom();
                UserDisplay to = mappingDecoratorContext.getTo();
                to.setFullName(from.getFirstName() + " " + from.getLastName());
            })
            .build();
    MappingDefinition mappingDefinition = new MappingDefinitionBuilder(SimpleEntity.class, SimpleEntityDisplay.class)
            .mapField("name", "name").withMappingStrategy(MappingStrategy.MAP_ALL)
            .build();
    val shapeShift = ShapeShiftBuilder()
        .withTransformer(DateToLongMappingTransformer(), default = true)
        .withTransformer(StringToListMappingTransformer())
        .build()
    ShapeShift shapeShift = new ShapeShiftBuilder()
            .withTransformer(new MappingTransformerRegistration(
                    Date.class,
                    Long.class,
                    new DateToLongMappingTransformer(),
                    true
            ))
            .withTransformer(new MappingTransformerRegistration(
                    String.class,
                    List.class,
                    new StringToListMappingTransformer(),
                    false
            ))
            .build();
    val shapeShift = ShapeShiftBuilder()
        .excludeDefaultTransformers()
        .build()
    ShapeShift shapeShift = new ShapeShiftBuilder()
            .excludeDefaultTransformers()
            .build();
    @DefaultMappingTarget(SimpleEntityDisplay::class)
    data class SimpleEntity(
        @MappedField
        val creationDate: Date,
    
        @MappedField(transformer = StringToListMappingTransformer::class, mapTo = "stringList")
        val commaDelimitedString: String
    )
    @DefaultMappingTarget(SimpleEntityDisplay.class)
    public class SimpleEntity {
        @MappedField
        private Date creationDate;
        @MappedField(transformer = StringToListMappingTransformer.class, mapTo = "stringList")
        private String commaDelimitedString;
        // Getters and Setters...
    }
    val mapper = mapper<SimpleEntity, SimpleEntityDisplay> {
        SimpleEntity::creationDate mappedTo SimpleEntityDisplay::creationDate
        SimpleEntity::commaDelimitedString mappedTo SimpleEntityDisplay::stringList withTransformer StringToListMappingTransformer::class
    }
    val mapper = mapper<SimpleEntity, SimpleEntityDisplay> {
        SimpleEntity::creationDate mappedTo SimpleEntityDisplay::creationDate
        SimpleEntity::commaDelimitedString mappedTo SimpleEntityDisplay::stringList withTransformer {
            it.originalValue?.split(",")
        }
    }
    MappingDefinition mappingDefinition = new MappingDefinitionBuilder(SimpleEntity.class, SimpleEntityDisplay.class)
            .mapField("creationDate", "creationDate")
            .mapField("commaDelimitedString", "stringList")
            .withTransformer(StringToListMappingTransformer.class)
            .build();
    MappingDefinition mappingDefinition = new MappingDefinitionBuilder(SimpleEntity.class, SimpleEntityDisplay.class)
            .mapField("creationDate", "creationDate")
            .mapField("commaDelimitedString", "stringList")
            .withTransformer(context -> context.getOriginalValue() != null
                    ? Arrays.asList(((String) context.getOriginalValue()).split(","))
                    : null)
            .build();
    @Test
    fun `test mapping for SimpleEntityDisplay`() {
        val shapeShift = ShapeShiftBuilder()
            .withTransformer(DateToLongTransformer(), default = true)
            .withTransformer(StringToCommaSeparatedStringListTransformer())
            .build()
        val simpleEntity = SimpleEntity(
                Date(),
                "one,two,three"
        )
        val result = shapeShift.map<SimpleEntityDisplay>(simpleEntity)
        expectThat(result.creationDate)
                .isEqualTo(simpleEntity.creationDate.time)
        expectThat(result.stringList)
                .isEqualTo(listOf("one", "two", "three"))
    }
    @Test
    public void testMappingForSimpleEntityDisplay() {
        ShapeShift shapeShift = new ShapeShiftBuilder()
                .excludeDefaultTransformers()
                .withTransformer(new MappingTransformerRegistration(
                        Date.class,
                        Long.class,
                        new DateToLongMappingTransformer(),
                        true
                ))
                .withTransformer(new MappingTransformerRegistration(
                        String.class,
                        List.class,
                        new StringToListMappingTransformer(),
                        false
                ))
                .build();
    
        SimpleEntity simpleEntity = new SimpleEntity();
        simpleEntity.setCreationDate(new Date());
        simpleEntity.setCommaDelimitedString("one,two,three");
    
        SimpleEntityDisplay result = shapeShift.map(simpleEntity, SimpleEntityDisplay.class);
    
        assertEquals(result.getCreationDate(), simpleEntity.getCreationDate().getTime());
        assertEquals(result.getStringList(), Arrays.asList("one", "two", "three"));
    }

    Annotations

    Annotations based usage of the ShapeShift library.

    Basic Mapping

    We start by defining two classes, our source class SimpleEntity and our destination class SimpleEntityDisplay.

    public class SimpleEntityDisplay {
        private String name = "";
        private String description = "";
    
        public String getName() {
    
    

    We can now start adding our annotations to the SimpleEntity class. In this example, we want to map the name and description fields to the name and description fields of the SimpleEntityDisplay class, but not the privateData field.

    To achieve this, we will use the @MappedField annotation on both of these fields. Additionally, we will define @DefaultMappingTarget on the SimpleEntity class, which will indicate that all fields annotated with @MappedField that do not specify a target should be mapped to the SimpleEntityDisplay class.

    @DefaultMappingTarget(SimpleEntityDisplay::class)
    data class SimpleEntity(
        @MappedField
        val name: String,
        @MappedField
        val description: String,
        val privateData: String
    
    @DefaultMappingTarget(SimpleEntityDisplay.class)
    public class SimpleEntity {
        @MappedField
        private String name;
        @MappedField
        private String description;
        private
    

    Default Mapping Target

    @DefaultMappingTarget on a class, indicates that all fields annotated with @MappedField that do not specify a target should be mapped to this class by default.

    Mapping Target

    The mapping target comes into play when you want to map a single source to multiple destinations. The target attribute is used to indicate to which class the field should be mapped. If no target is specified the target will be determined by the @DefaultMappingTarget on the class.

    In the above code name is mapped once to the SimpleEntityDisplay class using the default mapping target, and once to the SimpleEntityExport class using the target attribute.

    Map To Field

    By default each @MappedField is mapped to a field with the same name in the target class. To change it use the mapTo value. For example:

    Map From Field

    The field name to map the value from.

    Field Level

    When mapFrom is used at the field level, it allows for mapping of nested values.

    As you can see above, we use the mapFrom attribute to access the childName field in AdvancedChildEntity.

    And we use the mapTo attribute to map the values to the appropriate fields in AdvancedEntityDisplay.

    Type Level

    The @MappedField annotation can also be used at the type level. When used at the type level, the mapFrom attribute is required to indicate the name of the field to use, if left empty an exception will be thrown.

    Transformer

    Field transformers are a way to transform a field from one type to another when mapping it to a destination class. More about the ins-and-outs of transformers is available here:

    To configure a transformer for a field use the transformer attribute. The transformer attribute receives the transformer's class.

    Transformers must be registered to the ShapeShift instance in order to be used. More info about registering transformers is available in the transformers page.

    Auto Mapping

    Auto mapping is used to reduce the amount of boiler-place code required to configure mapping between two classes. More info about auto mapping is available here:

    Auto mapping can be added using the @AutoMapping annotation.

    The @AutoMapping annotation has two attributes:

    1. target - If no target is added then the auto mapping will be configured to any target class. It is possible to add multiple @AutoMapping annotation for multiple target classes.

    2. strategy - The auto mapping strategy. Default value NONE.

    Mapping Condition

    Conditions are used to determine wether a field should be mapped according to certain logic. More info about conditions is available here:

    A condition can be added to annotation mapping using the condition attribute.

    We mapped the name field and added a condition for the mapping.

    The condition receives context with the original value of the field and checks that it is not null or blank.

    Override Mapping Strategy

    The overrideMappingStrategy attribute allows you to override the default mapping strategy configured on the ShapeShift instance.

    More info about mapping strategy is available here:

    data class SimpleEntity(
        val name: String,
        val description: String,
        val privateData: String
    )
    public class SimpleEntity {
        private String name;
        private String description;
        private String privateData;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getDescription() {
            return description;
        }
    
        public void setDescription(String description) {
            this.description = description;
        }
    
        public String getPrivateData() {
            return privateData;
        }
    
        public void setPrivateData(String privateData) {
            this.privateData = privateData;
        }
    }
    data class SimpleEntityDisplay(
        val name: String = "",
        val description: String = ""
    )
    Transformers
    Transformers
    Auto Mapping
    Conditions
    Mapping Strategy
    @DefaultMappingTarget(SimpleEntityDisplay::class)
    data class SimpleEntity(
        @MappedField
        @MappedField(target = SimpleEntityExport::class)
        val name: String,
        @MappedField
        val description: String
    )
    @DefaultMappingTarget(SimpleEntityDisplay.class)
    public class SimpleEntity {
        @MappedField
        @MappedField(target = SimpleEntityExport.class)
        private String name;
        @MappedField
        private String description;
        // Getters and Setters...
    }
    data class SimpleEntityDisplay(
        val name: String = "",
        val description: String = ""
    )
    public class SimpleEntityDisplay {
        private String name = "";
        private String description = "";
        // Getters and Setters...
    }
    data class SimpleEntityExport(
        val name: String = ""
    )
    public class SimpleEntityExport {
        private String name = "";
        // Getters and Setters...
    }
    @DefaultMappingTarget(SimpleEntityDisplay::class)
    data class SimpleEntity(
        @MappedField(mapTo = "fullName")
        val name: String
    )
    @DefaultMappingTarget(SimpleEntityDisplay.class)
    public class SimpleEntity {
        @MappedField(mapTo = "fullName")
        private String name;
        // Getters and Setters...
    }
    data class SimpleEntityDisplay(
        val fullName: String = ""
    )
    public class SimpleEntityDisplay {
        private String fullName = "";
        // Getters and Setters...
    }
    @DefaultMappingTarget(AdvancedEntityDisplay::class)
    data class AdvancedEntity(
        // This field will be mapped to the "firstChildName" field in the default target class
        @MappedField(mapFrom = "childName", mapTo = "firstChildName")
        val firstChild: AdvancedChildEntity,
    
        // This field will be mapped to the "secondChildName" field in the default target class
        @MappedField(mapFrom = "childName", mapTo = "secondChildName")
        val secondChild: AdvancedChildEntity
    )
    @DefaultMappingTarget(AdvancedEntityDisplay.class)
    public class AdvancedEntity {
        // This field will be mapped to the "firstChildName" field in the default target class
        @MappedField(mapFrom = "childName", mapTo = "firstChildName")
        private AdvancedChildEntity firstChild;
    
        // This field will be mapped to the "secondChildName" field in the default target class
        @MappedField(mapFrom = "childName", mapTo = "secondChildName")
        private AdvancedChildEntity secondChild;
        
        // Getters and Setters...
    }
    data class AdvancedChildEntity(
        val childName: String
    )
    public class AdvancedChildEntity {
        private String childName;
        // Getters and Setters...
    }
    data class AdvancedEntityDisplay(
        val firstChildName: String = "",
        val secondChildName: String = ""
    )
    public class AdvancedEntityDisplay {
        private String firstChildName = "";
        private String secondChildName = "";
        // Getters and Setters...
    }
    @MappedField(target = SimpleEntityDisplay::class, mapFrom = "firstName")
    @MappedField(target = SimpleEntityDisplay::class, mapFrom = "lastName")
    data class SimpleEntity(
        val firstName: String,
        val lastName: String,
    )
    @MappedField(target = SimpleEntityDisplay.class, mapFrom = "firstName")
    @MappedField(target = SimpleEntityDisplay.class, mapFrom = "lastName")
    public class SimpleEntity {
        private String firstName;
        private String lastName;
        // Getters and Setters...
    }
    data class SimpleEntityDisplay(
        val firstName: String = "",
        val lastName: String = ""
    )
    public class SimpleEntityDisplay {
        private String firstName = "";
        private String lastName = "";
        // Getters and Setters...
    }
    @DefaultMappingTarget(SimpleEntityDisplay::class)
    data class SimpleEntity(
        @MappedField(transformer = StringToListMappingTransformer::class, mapTo = "stringList")
        val commaDelimitedString: String
    )
    @DefaultMappingTarget(SimpleEntityDisplay.class)
    public class SimpleEntity {
        @MappedField(transformer = StringToListMappingTransformer.class, mapTo = "stringList")
        private String commaDelimitedString;
        // Getters and Setters...
    }
    data class SimpleEntityDisplay(
        val stringList: List<String> = emptyList()
    )
    public class SimpleEntityDisplay {
        private List<String> stringList = new ArrayList<>();
        // Getters and Setters...
    }
    @AutoMapping(SimpleEntityDisplay::class, strategy = AutoMappingStrategy.BY_NAME)
    @DefaultMappingTarget(SimpleEntityDisplay::class)
    data class SimpleEntity(
        val id: String,
        val name: String,
        val birthDate: Date,
        val email: String,
        val telephone: String
    )
    @AutoMapping(target = SimpleEntityDisplay.class, strategy = AutoMappingStrategy.BY_NAME)
    @DefaultMappingTarget(SimpleEntityDisplay.class)
    public class SimpleEntity {
        private String id;
        private String name;
        private Date birthDate;
        private String email;
        private String telephone;
        // Getters and Setters...
    }
    @DefaultMappingTarget(SimpleEntityDisplay::class)
    data class SimpleEntity(
        @MappedField(condition = NotBlankStringCondition::class)
        val name: String
    )
    @DefaultMappingTarget(SimpleEntityDisplay.class)
    public class SimpleEntity {
        @MappedField(condition = NotBlankStringCondition.class)
        private String name;
        // Getters and Setters...
    }
    data class SimpleEntityDisplay(
        val name: String = ""
    )
    public class SimpleEntityDisplay {
        private String name = "";
        // Getters and Setters...
    }
    class NotBlankStringCondition : MappingCondition<String> {
        override fun isValid(context: MappingConditionContext<String>): Boolean {
            return !context.originalValue.isNullOrBlank()
        }
    }
    public class NotBlankStringCondition implements MappingCondition<String> {
        @Override
        public boolean isValid(@NonNull MappingConditionContext<String> context) {
            return context.getOriginalValue() != null && !context.getOriginalValue().trim().isEmpty();
        }
    }
    @DefaultMappingTarget(SimpleEntityDisplay::class)
    data class SimpleEntity(
        @MappedField(overrideMappingStrategy = MappingStrategy.MAP_ALL)
        val name: String
    )
    @DefaultMappingTarget(SimpleEntityDisplay.class)
    public class SimpleEntity {
        @MappedField(overrideMappingStrategy = MappingStrategy.MAP_ALL)
        private String name;
        // Getters and Setters...
    }
    return name;
    }
    public void setName(String name) {
    this.name = name;
    }
    public String getDescription() {
    return description;
    }
    public void setDescription(String description) {
    this.description = description;
    }
    }
    )
    String
    privateData;
    // Getters and Setters...
    }