/komap

Type Safe Entity Mapper

Primary LanguageKotlinApache License 2.0Apache-2.0

Komap

Type Safe Kotlin Entity Mapper

license
license
license

All what you need for mapping in most cases is a simple mapping whose implementation takes about one hundred lines.

This project contains one file which solves most popular entity mapping problems.

The simplest but same time extremely functional example below:

val mapper = mapping {
    plain<KUser, KUserDto> {
        strict(KUser::username, KUserDto::name)
        flexible(KUser::address, KUserDto::plainAddress)
    }
    smart<KAddress, String> {
        "$country $city $street"
    }
}

Let’s discuss what is there.

First of all we define space of a mapping DSL, inside it we write our mapping rules. There are two mapping rules for types: plain and smart. Plain is the typical rule when we create one to one mapping, we just map one field to other field. We use smart when we want to create some custom mapping from one type to another. For example, we flatten instance of KAddress class to simple String. In a smart’s lambda definition we work with a source instance like in a source class method, we have access to this keyword, but only to public api of the source class.

When we talk about plain rule we can use field rules for map one field of first entity to other field of second entity. For type safe control we divide two field rules: strict and flexible. I recommend use strict by default if you can. Strict rule checks types of source and destination property in compile time, otherwise flexible property rule will discover other (not equal) types in mapping time and if it isn’t found your mapper will throw an exception.

For usage of mapping rules we just invoke mapTo method:

val user = KUser("mr_holmes", KAddress("UK", "London", "Baker Street"))

val userDto: KUserDto = mapper.mapTo(user)

As result, userDto can be printed like this:

UserDto(name='mr_holmes', plainAddress='UK London Baker Street')

Expansion

For different mappers common mapping can exist or you can looking for way to extract a part of mapping to other file. Solution is expand function. It helps you to expand your mapping rules with other ones. Simple example is bellow.

val DateTimeMappings = mapping {
    smart<Date, LocalDate> {
        toInstant().atZone(ZoneId.systemDefault()).toLocalDate()
    }
    smart<Date, LocalTime> {
        toInstant().atZone(ZoneId.systemDefault()).toLocalTime()
    }
}

Define your common mapping (for example date mapping) and use it where necessary.

val mapper = mapping {
    expand(DateTimeMappings) //using predefined mappings

    plain<Order, LocalOrder> {
        strict(Order::name, LocalOrder::name)
        flexible(Order::date, LocalOrder::date) //Date -> LocalDate
        flexible(Order::time, LocalOrder::time) //Time -> LocalTime
    }
}

Java Compatibility

Of course, it works for java entity model. When we talk about Java we operate with getters and setters and changes are really minimalist.

plain<JUser, JUserDto> {
    strict(JUser::getUsername, JUserDto::setName)
    flexible(JUser::getAddress, JUserDto::setPlainAddress)
}

I want!

How to use it? Dependency is not necessary! Just copy one file to your project. You can rename some parts or package, it is up to you.

Issues

  1. Need to add context-dependent mapping. For example, "We use KAddress → String only if we map KUser to UserDto"

License

Copyright 2018 Ivan Osipov

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.