From 9cdba8cce0c3a814c3b6db89b79396f8c54b0d48 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Thu, 23 Jul 2020 15:00:49 +0900 Subject: [PATCH 01/33] =?UTF-8?q?=E3=83=98=E3=83=83=E3=83=80=E3=83=BC?= =?UTF-8?q?=E3=81=BE=E3=81=A7=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 README.ja.md diff --git a/README.ja.md b/README.ja.md new file mode 100644 index 0000000..17616a6 --- /dev/null +++ b/README.ja.md @@ -0,0 +1,7 @@ +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) +[![CircleCI](https://circleci.com/gh/ProjectMapK/KMapper.svg?style=svg)](https://circleci.com/gh/ProjectMapK/KMapper) +[![](https://jitci.com/gh/ProjectMapK/KMapper/svg)](https://jitci.com/gh/ProjectMapK/KMapper) +[![codecov](https://codecov.io/gh/ProjectMapK/KMapper/branch/master/graph/badge.svg)](https://codecov.io/gh/ProjectMapK/KMapper) + +KMapper +==== From ed28153d86b3cb4cfa6af41c00d4037f1a19499b Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Thu, 23 Jul 2020 15:43:54 +0900 Subject: [PATCH 02/33] =?UTF-8?q?=E3=83=87=E3=83=A2=E3=82=B3=E3=83=BC?= =?UTF-8?q?=E3=83=89=E3=81=BE=E3=81=A7=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.ja.md b/README.ja.md index 17616a6..5624e37 100644 --- a/README.ja.md +++ b/README.ja.md @@ -5,3 +5,28 @@ KMapper ==== +`KMapper`は`Kotlin`向けのマッパーライブラリであり、以下の機能を提供します。 + +- オブジェクトや`Map`、`Pair`をソースとした`Bean`マッピング +- `Kotlin`のリフレクションを用いた関数呼び出しベースの安全なマッピング +- 豊富な機能による、より柔軟かつ労力の少ないマッピング + +## デモコード +手動でマッピングコードを書いた場合と`KMapper`を用いた場合を比較します。 +手動で書く場合引数が多ければ多いほど記述がかさみますが、`KMapper`を用いることで殆どコードを書かずにマッピングを行えます。 +また、外部の設定ファイルは一切必要ありません。 + +```kotlin +// 手動でマッピングを行う場合 +val dst = Dst( + param1 = src.param1, + param2 = src.param2, + param3 = src.param3, + param4 = src.param4, + param5 = src.param5, + ... +) + +// KMapperを用いる場合 +val dst = KMapper(::Dst).map(src) +``` From f92f6bf183bcd54a898670bb2f43d2f835c1ccf8 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Thu, 23 Jul 2020 15:46:38 +0900 Subject: [PATCH 03/33] =?UTF-8?q?=E3=82=A4=E3=83=B3=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=83=BC=E3=83=AB=E6=96=B9=E6=B3=95=E3=81=BE=E3=81=A7=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/README.ja.md b/README.ja.md index 5624e37..babb10f 100644 --- a/README.ja.md +++ b/README.ja.md @@ -30,3 +30,33 @@ val dst = Dst( // KMapperを用いる場合 val dst = KMapper(::Dst).map(src) ``` + +## インストール方法 +`KMapper`は`JitPack`にて公開しており、`Maven`や`Gradle`といったビルドツールから手軽に利用できます。 +各ツールでの正確なインストール方法については下記をご参照ください。 + +- [ProjectMapK / KMapper](https://jitpack.io/#ProjectMapK/KMapper) + +### Mavenでのインストール方法 +以下は`Maven`でのインストール例です。 + +**1. JitPackのリポジトリへの参照を追加する** + +```xml + + + jitpack.io + https://jitpack.io + + +``` + +**2. dependencyを追加する** + +```xml + + com.github.ProjectMapK + KMapper + Tag + +``` From 98cf3d0f3a54d6c2a8097fcd640771ab05bfe60a Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Thu, 23 Jul 2020 15:50:14 +0900 Subject: [PATCH 04/33] =?UTF-8?q?=E5=8B=95=E4=BD=9C=E5=8E=9F=E7=90=86?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.ja.md b/README.ja.md index babb10f..2e9b359 100644 --- a/README.ja.md +++ b/README.ja.md @@ -60,3 +60,15 @@ val dst = KMapper(::Dst).map(src) Tag ``` + +## 動作原理 +`KMapper`は以下のように動作します。 + +1. 呼び出し対象の`KFunction`を取り出す +2. `KFunction`を解析し、必要な引数とその取り出し方を決定する +3. 入力からそれぞれの引数に対応する値の取り出しを行い、`KFunction`を呼び出す + +最終的にはコンストラクタや`companion object`に定義したファクトリーメソッドなどを呼び出してマッピングを行うため、結果は`Kotlin`上の引数・`nullability`等の制約に従います。 +つまり、`Kotlin`の`null`安全が壊れることによる実行時エラーは発生しません(ただし、型引数の`nullability`に関しては`null`安全が壊れる場合が有ります)。 + +また、`Kotlin`特有の機能であるデフォルト引数等にも対応しています。 From 810b709bd8eb5761d3212994e525ae99f51da2ba Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Thu, 23 Jul 2020 16:16:37 +0900 Subject: [PATCH 05/33] =?UTF-8?q?3=E7=A8=AE=E9=A1=9E=E3=81=AE=E3=83=9E?= =?UTF-8?q?=E3=83=83=E3=83=94=E3=83=B3=E3=82=B0=E3=82=AF=E3=83=A9=E3=82=B9?= =?UTF-8?q?=E3=81=AB=E9=96=A2=E3=81=97=E3=81=A6=E8=BF=BD=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.ja.md b/README.ja.md index 2e9b359..f7a8744 100644 --- a/README.ja.md +++ b/README.ja.md @@ -72,3 +72,25 @@ val dst = KMapper(::Dst).map(src) つまり、`Kotlin`の`null`安全が壊れることによる実行時エラーは発生しません(ただし、型引数の`nullability`に関しては`null`安全が壊れる場合が有ります)。 また、`Kotlin`特有の機能であるデフォルト引数等にも対応しています。 + +## マッパークラスの種類について +このプロジェクトでは以下の3種類のマッパークラスを提供しています。 + +- `KMapper` +- `PlainKMapper` +- `BoundKMapper` + +以下にそれぞれの特徴と使いどころをまとめます。 +また、これ以降共通の機能に関しては`KMapper`を例に説明を行います。 + +### KMapper +`KMapper`はこのプロジェクトの基本となるマッパークラスです。 +内部ではキャッシュを用いたマッピングの高速化などを行っているため、マッパーを使い回す形での利用に向きます。 + +### PlainKMapper +`PlainKMapper`は`KMapper`からキャッシュ機能を取り除いたマッパークラスです。 +複数回マッピングを行った場合の性能は`KMapper`に劣りますが、キャッシュ処理のオーバーヘッドが無いため、マッパーを使い捨てる形での利用に向きます。 + +### BoundKMapper +`BoundKMapper`はソースとなるクラスが1つに限定できる場合に利用できるマッピングクラスです。 +`KMapper`に比べ高速に動作します。 From 597d27b20414e73617dbb348e164e037a4360d1c Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Thu, 23 Jul 2020 17:58:17 +0900 Subject: [PATCH 06/33] =?UTF-8?q?method=20reference(KFunction)=E3=81=8B?= =?UTF-8?q?=E3=82=89=E3=81=AE=E5=88=9D=E6=9C=9F=E5=8C=96=E3=81=BE=E3=81=A7?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/README.ja.md b/README.ja.md index f7a8744..a7660b2 100644 --- a/README.ja.md +++ b/README.ja.md @@ -94,3 +94,31 @@ val dst = KMapper(::Dst).map(src) ### BoundKMapper `BoundKMapper`はソースとなるクラスが1つに限定できる場合に利用できるマッピングクラスです。 `KMapper`に比べ高速に動作します。 + +## KMapperの初期化 +`KMapper`は呼び出し対象の`method reference(KFunction)`、またはマッピング先の`KClass`から初期化できます。 + +### method reference(KFunction)からの初期化 +プライマリコンストラクタを呼び出し対象とする場合、以下のように初期化を行うことができます。 +ただし、この例では簡単のため`BoundKMapper`の初期化をダミーコンストラクタによって行っています。 + +```kotlin +data class Dst( + foo: String, + bar: String, + baz: Int?, + + ... + +) + +// コンストラクタのメソッドリファレンスを取得 +val dstConstructor: KFunction = ::Dst + +// KMapperの場合 +val kMapper: KMapper = KMapper(dstConstructor) +// PlainKMapperの場合 +val plainMapper: PlainKMapper = PlainKMapper(dstConstructor) +// BoundKMapperの場合(ダミーコンストラクタ利用) +val boundKMapper: BoundKMapper = BoundKMapper(dstConstructor) +``` From 8401d37caa7f8852ba0d02e150c6b7cbee92de29 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Thu, 23 Jul 2020 18:06:15 +0900 Subject: [PATCH 07/33] =?UTF-8?q?=E5=BE=AE=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.ja.md b/README.ja.md index a7660b2..814b75a 100644 --- a/README.ja.md +++ b/README.ja.md @@ -96,11 +96,13 @@ val dst = KMapper(::Dst).map(src) `KMapper`に比べ高速に動作します。 ## KMapperの初期化 -`KMapper`は呼び出し対象の`method reference(KFunction)`、またはマッピング先の`KClass`から初期化できます。 +`KMapper`は呼び出し対象の`method reference(KFunction)`、またはマッピング先の`KClass`から初期化できます。 + +以下にそれぞれの初期化方法をまとめます。 +ただし、`BoundKMapper`の初期化の内可能なものは全てダミーコンストラクタによって簡略化した例を示します。 ### method reference(KFunction)からの初期化 -プライマリコンストラクタを呼び出し対象とする場合、以下のように初期化を行うことができます。 -ただし、この例では簡単のため`BoundKMapper`の初期化をダミーコンストラクタによって行っています。 +プライマリコンストラクタを呼び出し対象とする場合、以下のように初期化を行うことができます。 ```kotlin data class Dst( @@ -119,6 +121,6 @@ val dstConstructor: KFunction = ::Dst val kMapper: KMapper = KMapper(dstConstructor) // PlainKMapperの場合 val plainMapper: PlainKMapper = PlainKMapper(dstConstructor) -// BoundKMapperの場合(ダミーコンストラクタ利用) +// BoundKMapperの場合 val boundKMapper: BoundKMapper = BoundKMapper(dstConstructor) ``` From 567914295810863778826b746a8ca6864413de69 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Thu, 23 Jul 2020 18:09:46 +0900 Subject: [PATCH 08/33] =?UTF-8?q?KClass=E3=81=8B=E3=82=89=E3=81=AE?= =?UTF-8?q?=E5=88=9D=E6=9C=9F=E5=8C=96=E3=82=92=E8=BF=BD=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.ja.md b/README.ja.md index 814b75a..a37defb 100644 --- a/README.ja.md +++ b/README.ja.md @@ -124,3 +124,29 @@ val plainMapper: PlainKMapper = PlainKMapper(dstConstructor) // BoundKMapperの場合 val boundKMapper: BoundKMapper = BoundKMapper(dstConstructor) ``` + +### KClassからの初期化 +`KMapper`は`KClass`からも初期化できます。 +デフォルトではプライマリーコンストラクタが呼び出し対象になります。 + +```kotlin +data class Dst(...) + +// KMapperの場合 +val kMapper: KMapper = KMapper(Dst::class) +// PlainKMapperの場合 +val plainMapper: PlainKMapper = PlainKMapper(Dst::class) +// BoundKMapperの場合 +val boundKMapper: BoundKMapper = BoundKMapper(Dst::class, Src::class) +``` + +ダミーコンストラクタを用い、かつジェネリクスを省略することで、それぞれ以下のようにも書けます。 + +```kotlin +// KMapperの場合 +val kMapper: KMapper = KMapper() +// PlainKMapperの場合 +val plainMapper: PlainKMapper = PlainKMapper() +// BoundKMapperの場合 +val boundKMapper: BoundKMapper = BoundKMapper() +``` From 9c20c48213b753b6caeec9b40cb61ad39516b871 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Thu, 23 Jul 2020 18:11:29 +0900 Subject: [PATCH 09/33] =?UTF-8?q?KConstructor=E3=82=A2=E3=83=8E=E3=83=86?= =?UTF-8?q?=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AB=E3=82=88=E3=82=8B?= =?UTF-8?q?=E5=91=BC=E3=81=B3=E5=87=BA=E3=81=97=E5=AF=BE=E8=B1=A1=E6=8C=87?= =?UTF-8?q?=E5=AE=9A=E3=82=92=E8=BF=BD=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/README.ja.md b/README.ja.md index a37defb..755af63 100644 --- a/README.ja.md +++ b/README.ja.md @@ -150,3 +150,32 @@ val plainMapper: PlainKMapper = PlainKMapper() // BoundKMapperの場合 val boundKMapper: BoundKMapper = BoundKMapper() ``` + +#### KConstructorアノテーションによる呼び出し対象指定 +`KClass`から初期化を行う場合、全てのマッパークラスでは`KConstructor`アノテーションを用いて呼び出し対象の関数を指定することができます。 + +以下の例ではセカンダリーコンストラクタが呼び出されます。 + +```kotlin +data class Dst(...) { + @KConstructor + constructor(...) : this(...) +} + +val mapper: KMapper = KMapper(Dst::class) +``` + +同様に、以下の例ではファクトリーメソッドが呼び出されます。 + +```kotlin +data class Dst(...) { + companion object { + @KConstructor + fun factory(...): Dst { + ... + } + } +} + +val mapper: KMapper = KMapper(Dst::class) +``` From 333dbdcec8d6746887074b532b0abea3e6367515 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Thu, 23 Jul 2020 18:33:02 +0900 Subject: [PATCH 10/33] =?UTF-8?q?=E3=83=87=E3=83=A2=E3=82=B3=E3=83=BC?= =?UTF-8?q?=E3=83=89=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.ja.md b/README.ja.md index 755af63..ee2961e 100644 --- a/README.ja.md +++ b/README.ja.md @@ -31,6 +31,18 @@ val dst = Dst( val dst = KMapper(::Dst).map(src) ``` +ソースは1つに限らず、複数のオブジェクトや、`Pair`、`Map`等を指定することもできます。 + +```kotlin +"" +val dst = KMapper(::Dst).map( + "param1" to "value of param1", + mapOf("param2" to 1, "param3" to 2L), + src1, + src2 +) +``` + ## インストール方法 `KMapper`は`JitPack`にて公開しており、`Maven`や`Gradle`といったビルドツールから手軽に利用できます。 各ツールでの正確なインストール方法については下記をご参照ください。 From 0a3887240c097f026f5c989adfb7f45a806b70ee Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Thu, 23 Jul 2020 19:33:46 +0900 Subject: [PATCH 11/33] =?UTF-8?q?=E5=BC=95=E6=95=B0=E3=81=AE=E3=82=BB?= =?UTF-8?q?=E3=83=83=E3=83=88=E3=82=A2=E3=83=83=E3=83=97=E3=81=AB=E3=81=A4?= =?UTF-8?q?=E3=81=84=E3=81=A6=E8=BF=BD=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/README.ja.md b/README.ja.md index ee2961e..f9ec225 100644 --- a/README.ja.md +++ b/README.ja.md @@ -191,3 +191,54 @@ data class Dst(...) { val mapper: KMapper = KMapper(Dst::class) ``` + +## 引数のセットアップ + +### 引数読み出しの対象 +`KMapper`は、オブジェクトの`public`フィールド、もしくは`Pair`、`Map`のプロパティを読み出しの対象とすることができます。 + +### 引数のセットアップ +`KMapper`は、まず`parameterClazz.isSuperclassOf(inputClazz)`で入力が引数に設定可能かを判定し、そのままでは設定できない場合は変換処理を行います。 + +#### 引数の変換処理 +`KMapper`は、以下の順序で変換内容のチェック及び変換処理を行います。 + +**1. アノテーションによる変換処理の確認** +まず初めに、入力のクラスに対応する、`KConvertBy`アノテーションや`KConverter`アノテーションによって指定された変換処理が無いかを確認します。 + +**2. Enumへの変換可否の確認** +入力が`String`で、かつ引数が`Enum`だった場合、入力と対応する`name`を持つ`Enum`への変換を試みます。 + +**3. 文字列への変換可否の確認** +引数が`String`の場合、入力を`toString`します。 + +**4. マッパークラスを用いた変換処理** +ここまでの変換条件に合致しなかった場合、マッパークラスを用いてネストした変換処理を行います。 +このマッピング処理には、`PlainKMapper`は`PlainKMapper`を、それ以外は`BoundKMapper`を用います。 + +これによって、以下のようなネストしたマッピングを行うことができます。 + +```kotlin +data class InnerDst(val foo: Int, val bar: Int) +data class Dst(val param: InnerDst) + +data class InnerSrc(val foo: Int, val bar: Int) +data class Src(val param: InnerSrc) + +val src = Src(InnerSrc(1, 2)) +val dst = KMapper(::Dst).map(src) + +println(dst.param) // -> InnerDst(foo=1, bar=2) +``` + +### 入力の優先度 +`KMapper`では、基本的に先に入った入力可能な引数を優先します。 +例えば、以下の例では`param1`として先に`value1`が指定されているため、`"param1" to "value2"`は無視されます。 + +```kotlin +val mapper: KMapper = ... + +val dst = mapper.map("param1" to "value1", "param1" to "value2") +``` + +ただし、`KParameterRequireNonNull`アノテーションが指定された引数に対応する入力として`null`が指定された場合、その入力は無視され、後から入った引数が優先されます。 From 617b802f86d70db19414ebf94565f0228f437c9c Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Thu, 23 Jul 2020 23:46:27 +0900 Subject: [PATCH 12/33] =?UTF-8?q?=E8=A9=B3=E7=B4=B0=E3=81=AA=E4=BD=BF?= =?UTF-8?q?=E3=81=84=E6=96=B9=E3=81=AE=E9=A1=8C=E3=81=BE=E3=81=A7=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.ja.md b/README.ja.md index f9ec225..c5e129c 100644 --- a/README.ja.md +++ b/README.ja.md @@ -192,6 +192,14 @@ data class Dst(...) { val mapper: KMapper = KMapper(Dst::class) ``` +## 詳細な使い方 + +### マッピング時の値の変換 + +### 引数名・フィールド名の変換 + +### その他機能 + ## 引数のセットアップ ### 引数読み出しの対象 From 9c821abb06e1fd33592019f52cbc19348335405e Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Thu, 23 Jul 2020 23:53:36 +0900 Subject: [PATCH 13/33] =?UTF-8?q?=E3=83=87=E3=83=95=E3=82=A9=E3=83=AB?= =?UTF-8?q?=E3=83=88=E5=BC=95=E6=95=B0=E5=91=A8=E3=82=8A=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.ja.md b/README.ja.md index c5e129c..133b4f1 100644 --- a/README.ja.md +++ b/README.ja.md @@ -199,6 +199,32 @@ val mapper: KMapper = KMapper(Dst::class) ### 引数名・フィールド名の変換 ### その他機能 +#### 制御してデフォルト引数を用いる +`KMapper`では、引数が指定されていなかった場合デフォルト引数を用います。 +また、引数が指定されていた場合でも、それを用いるか制御することができます。 + +##### 必ずデフォルト引数を用いる +必ずデフォルト引数を用いたい場合、`KUseDefaultArgument`アノテーションを利用できます。 + +```kotlin +class Foo( + ..., + @KUseDefaultArgument + val description: String = "" +) +``` + +##### 対応する内容が全てnullの場合デフォルト引数を用いる +`KParameterRequireNonNull`アノテーションを指定することで、引数として`non null`な値が指定されるまで入力をスキップします。 +これを利用することで、対応する内容が全て`null`の場合デフォルト引数を用いるという挙動が実現できます。 + +```kotlin +class Foo( + ..., + @KParameterRequireNonNull + val description: String = "" +) +``` ## 引数のセットアップ From 5eeb1aeb1ba79fa86e107a11512cc5de2f89f81e Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Fri, 24 Jul 2020 00:02:56 +0900 Subject: [PATCH 14/33] =?UTF-8?q?=E5=BC=95=E6=95=B0=E3=81=AE=E3=82=BB?= =?UTF-8?q?=E3=83=83=E3=83=88=E3=82=A2=E3=83=83=E3=83=97=E5=91=A8=E3=82=8A?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.ja.md b/README.ja.md index 133b4f1..cee6be6 100644 --- a/README.ja.md +++ b/README.ja.md @@ -232,7 +232,13 @@ class Foo( `KMapper`は、オブジェクトの`public`フィールド、もしくは`Pair`、`Map`のプロパティを読み出しの対象とすることができます。 ### 引数のセットアップ -`KMapper`は、まず`parameterClazz.isSuperclassOf(inputClazz)`で入力が引数に設定可能かを判定し、そのままでは設定できない場合は変換処理を行います。 +`KMapper`は、値が`null`でなければセットアップ処理を行います。 +セットアップ処理では、まず`parameterClazz.isSuperclassOf(inputClazz)`で入力が引数に設定可能かを判定し、そのままでは設定できない場合は後述する変換処理を行い、結果を引数とします。 + +値が`null`だった場合は`KParameterRequireNonNull`アノテーションの有無を確認し、設定されていればセットアップ処理をスキップ、されていなければ`null`をそのまま引数とします。 + +`KUseDefaultArgument`アノテーションが設定されていたり、`KParameterRequireNonNull`アノテーションによって全ての入力がスキップされた場合、デフォルト引数が用いられます。 +ここでデフォルト引数が利用できなかった場合は実行時エラーとなります。 #### 引数の変換処理 `KMapper`は、以下の順序で変換内容のチェック及び変換処理を行います。 From 604af34d338b0c235b51fad04aab737aab615fc6 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Fri, 24 Jul 2020 00:06:51 +0900 Subject: [PATCH 15/33] =?UTF-8?q?=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.ja.md b/README.ja.md index cee6be6..689aed1 100644 --- a/README.ja.md +++ b/README.ja.md @@ -196,7 +196,8 @@ val mapper: KMapper = KMapper(Dst::class) ### マッピング時の値の変換 -### 引数名・フィールド名の変換 +### マッピング時に用いる引数名・フィールド名の設定 + ### その他機能 #### 制御してデフォルト引数を用いる From 8046ec34e1d4e9918030ff1657e231c36636742e Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Fri, 24 Jul 2020 00:13:44 +0900 Subject: [PATCH 16/33] =?UTF-8?q?=E5=BC=95=E6=95=B0=E3=81=AE=E5=A4=89?= =?UTF-8?q?=E6=8F=9B=E3=81=BE=E3=81=A7=E8=BF=BD=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/README.ja.md b/README.ja.md index 689aed1..9acd8f8 100644 --- a/README.ja.md +++ b/README.ja.md @@ -197,7 +197,54 @@ val mapper: KMapper = KMapper(Dst::class) ### マッピング時の値の変換 ### マッピング時に用いる引数名・フィールド名の設定 +#### 引数名の変換 +`KMapper`は、デフォルトでは引数名に対応するフィールドをそのまま探します。 +一方、引数の命名規則がキャメルケースかつソースの命名規則がスネークケースというような場合、このままでは一致を見ることができません。 +このような一定の変換が要求される状況には、命名の変更関数を初期化時に設定することで対応できます。 + +```kotlin +data class Dst( + fooFoo: String, + barBar: String, + bazBaz: Int? +) + +val mapper: KMapper = KMapper(::Dst) { fieldName: String -> + /* 命名変換処理 */ +} + +// 例えばスネークケースへの変換関数を渡すことで、以下のような入力にも対応できる +val dst = mapper.map(mapOf( + "foo_foo" to "foo", + "bar_bar" to "bar", + "baz_baz" to 3 +)) +``` + +また、当然ながらラムダ内で任意の変換処理を行うこともできます。 + +#### 実際の変換処理 +`KRowMapper`では命名変換処理を提供していませんが、`Spring`やそれを用いたプロジェクトの中で用いられるライブラリでは命名変換処理が提供されている場合が有ります。 +`Jackson`、`Guava`の2つのライブラリで実際に「キャメルケース -> スネークケース」の変換処理を渡すサンプルコードを示します。 + +##### Jackson +```kotlin +import com.fasterxml.jackson.databind.PropertyNamingStrategy + +val parameterNameConverter: (String) -> String = PropertyNamingStrategy.SnakeCaseStrategy()::translate +val mapper: KRowMapper = KRowMapper(::Dst, parameterNameConverter) +``` + +##### Guava +```kotlin +import com.google.common.base.CaseFormat + +val parameterNameConverter: (String) -> String = { fieldName: String -> + CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, fieldName) +} +val mapper: KRowMapper = KRowMapper(::Dst, parameterNameConverter) +``` ### その他機能 #### 制御してデフォルト引数を用いる From 2d6da8d9cdb0054173d047d23de6bcbbec4ab171 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Fri, 24 Jul 2020 00:18:04 +0900 Subject: [PATCH 17/33] =?UTF-8?q?=E5=89=8D=E6=AE=B5=E3=81=AE=E6=9B=B8?= =?UTF-8?q?=E3=81=8D=E7=9B=B4=E3=81=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/README.ja.md b/README.ja.md index 9acd8f8..2b28887 100644 --- a/README.ja.md +++ b/README.ja.md @@ -197,11 +197,14 @@ val mapper: KMapper = KMapper(Dst::class) ### マッピング時の値の変換 ### マッピング時に用いる引数名・フィールド名の設定 -#### 引数名の変換 -`KMapper`は、デフォルトでは引数名に対応するフィールドをそのまま探します。 -一方、引数の命名規則がキャメルケースかつソースの命名規則がスネークケースというような場合、このままでは一致を見ることができません。 +`KMapper`は、デフォルトでは引数名に対応する名前のフィールドをソースからそのまま探します。 +一方、引数名とソースで違う名前を用いたいという場合も有ります。 + +`KMapper`では、そのような状況に対応するため、マッピング時に用いる引数名・フィールド名を設定するいくつかの機能を提供しています。 -このような一定の変換が要求される状況には、命名の変更関数を初期化時に設定することで対応できます。 +#### 引数名の変換 +`KMapper`では、初期化時に引数名の変換関数を設定することができます。 +例えば引数の命名規則がキャメルケースかつソースの命名規則がスネークケースというような、一定の変換が要求される状況に対応することができます。 ```kotlin data class Dst( From 4a64e13395998208f7f66626a2b55182c073e9a5 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Fri, 24 Jul 2020 00:24:23 +0900 Subject: [PATCH 18/33] =?UTF-8?q?=E6=9B=B8=E3=81=8D=E6=8F=9B=E3=81=88?= =?UTF-8?q?=E5=BF=98=E3=82=8C=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.ja.md b/README.ja.md index 2b28887..14e5138 100644 --- a/README.ja.md +++ b/README.ja.md @@ -228,7 +228,7 @@ val dst = mapper.map(mapOf( また、当然ながらラムダ内で任意の変換処理を行うこともできます。 #### 実際の変換処理 -`KRowMapper`では命名変換処理を提供していませんが、`Spring`やそれを用いたプロジェクトの中で用いられるライブラリでは命名変換処理が提供されている場合が有ります。 +`KMapper`では命名変換処理を提供していませんが、プロジェクトでよく用いられるライブラリでも命名変換処理が提供されている場合が有ります。 `Jackson`、`Guava`の2つのライブラリで実際に「キャメルケース -> スネークケース」の変換処理を渡すサンプルコードを示します。 ##### Jackson @@ -236,7 +236,7 @@ val dst = mapper.map(mapOf( import com.fasterxml.jackson.databind.PropertyNamingStrategy val parameterNameConverter: (String) -> String = PropertyNamingStrategy.SnakeCaseStrategy()::translate -val mapper: KRowMapper = KRowMapper(::Dst, parameterNameConverter) +val mapper: KMapper = KMapper(::Dst, parameterNameConverter) ``` ##### Guava @@ -246,7 +246,7 @@ import com.google.common.base.CaseFormat val parameterNameConverter: (String) -> String = { fieldName: String -> CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, fieldName) } -val mapper: KRowMapper = KRowMapper(::Dst, parameterNameConverter) +val mapper: KMapper = KMapper(::Dst, parameterNameConverter) ``` ### その他機能 From c3b216eec341c69ad86601a8eabcfd31170b75b5 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Fri, 24 Jul 2020 00:28:14 +0900 Subject: [PATCH 19/33] =?UTF-8?q?KGetterAlias=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.ja.md b/README.ja.md index 14e5138..63e8f16 100644 --- a/README.ja.md +++ b/README.ja.md @@ -249,6 +249,23 @@ val parameterNameConverter: (String) -> String = { fieldName: String -> val mapper: KMapper = KMapper(::Dst, parameterNameConverter) ``` +#### ゲッターにエイリアスを設定する +以下のようなコードで、マッピング時にのみ`Scr`クラスの`_foo`フィールドの名前を変更する場合、`KGetterAlias`アノテーションを用いるのが最適です。 + +```kotlin +data class Dst(val foo: Int) +data class Src(val _foo: Int) +``` + +実際に付与すると以下のようになります。 + +```kotlin +data class Src( + @get:KGetterAlias("foo") + val _foo: Int +) +``` + ### その他機能 #### 制御してデフォルト引数を用いる `KMapper`では、引数が指定されていなかった場合デフォルト引数を用います。 From 2ac1c4f280b4cf320353fedb8d69b25a141cbed5 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Fri, 24 Jul 2020 00:30:39 +0900 Subject: [PATCH 20/33] =?UTF-8?q?KParameterAlias=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.ja.md b/README.ja.md index 63e8f16..116c0c6 100644 --- a/README.ja.md +++ b/README.ja.md @@ -266,6 +266,23 @@ data class Src( ) ``` +#### 引数名にエイリアスを設定する +以下のようなコードで、マッピング時にのみ`Dst`クラスの`_bar`フィールドの名前を変更する場合、`KParameterAlias`アノテーションを用いるのが最適です。 + +```kotlin +data class Dst(val _bar: Int) +data class Src(val bar: Int) +``` + +実際に付与すると以下のようになります。 + +```kotlin +data class Dst( + @KParameterAlias("bar") + val _bar: Int +) +``` + ### その他機能 #### 制御してデフォルト引数を用いる `KMapper`では、引数が指定されていなかった場合デフォルト引数を用います。 From 201d464ef609236bc46798120a867f61a0fe706d Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Fri, 24 Jul 2020 00:34:38 +0900 Subject: [PATCH 21/33] =?UTF-8?q?=E5=BE=AE=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.ja.md b/README.ja.md index 116c0c6..7e6652f 100644 --- a/README.ja.md +++ b/README.ja.md @@ -328,7 +328,7 @@ class Foo( #### 引数の変換処理 `KMapper`は、以下の順序で変換内容のチェック及び変換処理を行います。 -**1. アノテーションによる変換処理の確認** +**1. アノテーションによる変換処理の指定の確認** まず初めに、入力のクラスに対応する、`KConvertBy`アノテーションや`KConverter`アノテーションによって指定された変換処理が無いかを確認します。 **2. Enumへの変換可否の確認** From e2c7cb6b921331c7f336b6a76c01c36aaaef6e2c Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Fri, 24 Jul 2020 01:01:39 +0900 Subject: [PATCH 22/33] =?UTF-8?q?=E3=83=8D=E3=82=B9=E3=83=88=E3=81=97?= =?UTF-8?q?=E3=81=9F=E3=83=9E=E3=83=83=E3=83=94=E3=83=B3=E3=82=B0=E3=81=BE?= =?UTF-8?q?=E3=81=A7=E8=BF=BD=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ついでに構成の整理 --- README.ja.md | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/README.ja.md b/README.ja.md index 7e6652f..36cd9a0 100644 --- a/README.ja.md +++ b/README.ja.md @@ -195,6 +195,32 @@ val mapper: KMapper = KMapper(Dst::class) ## 詳細な使い方 ### マッピング時の値の変換 +マッピングを行うに当たり、入力の型を別の型に変換したい場合が有ります。 +`KMapper`では、そのような状況に対応するため、豊富な変換機能を提供しています。 + +#### デフォルトで利用可能な変換 +いくつかの変換機能は、特別な記述無しに利用することができます。 + +##### ネストしたマッピング +引数をそのまま用いることができず、かつその他の変換も行えない場合、`KMapper`は内部でマッピングクラスを用い、1対1マッピングを試みます。 +これによって、デフォルトで以下のようなネストしたマッピングを行うことができます。 + +```kotlin +data class InnerDst(val foo: Int, val bar: Int) +data class Dst(val param: InnerDst) + +data class InnerSrc(val foo: Int, val bar: Int) +data class Src(val param: InnerSrc) + +val src = Src(InnerSrc(1, 2)) +val dst = KMapper(::Dst).map(src) + +println(dst.param) // -> InnerDst(foo=1, bar=2) +``` + +###### ネストしたマッピングに用いられる関数の指定 +ネストしたマッピングは、`KMapper`をクラスから指定した場合と同様に行われます。 +このため、`KConstructor`アノテーションを用いて呼び出し対象を指定することができます。 ### マッピング時に用いる引数名・フィールド名の設定 `KMapper`は、デフォルトでは引数名に対応する名前のフィールドをソースからそのまま探します。 @@ -341,21 +367,6 @@ class Foo( ここまでの変換条件に合致しなかった場合、マッパークラスを用いてネストした変換処理を行います。 このマッピング処理には、`PlainKMapper`は`PlainKMapper`を、それ以外は`BoundKMapper`を用います。 -これによって、以下のようなネストしたマッピングを行うことができます。 - -```kotlin -data class InnerDst(val foo: Int, val bar: Int) -data class Dst(val param: InnerDst) - -data class InnerSrc(val foo: Int, val bar: Int) -data class Src(val param: InnerSrc) - -val src = Src(InnerSrc(1, 2)) -val dst = KMapper(::Dst).map(src) - -println(dst.param) // -> InnerDst(foo=1, bar=2) -``` - ### 入力の優先度 `KMapper`では、基本的に先に入った入力可能な引数を優先します。 例えば、以下の例では`param1`として先に`value1`が指定されているため、`"param1" to "value2"`は無視されます。 From e7fe2576083d3813a4756fd41a57b364dcd391b1 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Fri, 24 Jul 2020 01:08:10 +0900 Subject: [PATCH 23/33] =?UTF-8?q?=E5=A4=89=E6=8F=9B=E3=81=AE=E7=99=BA?= =?UTF-8?q?=E7=94=9F=E6=9D=A1=E4=BB=B6=E3=82=92=E8=BF=BD=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.ja.md b/README.ja.md index 36cd9a0..ef1e771 100644 --- a/README.ja.md +++ b/README.ja.md @@ -198,6 +198,12 @@ val mapper: KMapper = KMapper(Dst::class) マッピングを行うに当たり、入力の型を別の型に変換したい場合が有ります。 `KMapper`では、そのような状況に対応するため、豊富な変換機能を提供しています。 +ただし、この変換処理は以下の条件でのみ行われます。 + +- 入力が非`null` + - `null`が絡む場合は`KParameterRequireNonNull`アノテーションとデフォルト引数を組み合わせることを推奨します +- 入力が引数に直接代入できない + #### デフォルトで利用可能な変換 いくつかの変換機能は、特別な記述無しに利用することができます。 From baa1a8bc34a3187ca5b9081278898e8565a913a0 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Fri, 24 Jul 2020 01:15:46 +0900 Subject: [PATCH 24/33] =?UTF-8?q?=E5=BE=AE=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.ja.md b/README.ja.md index ef1e771..f535fc8 100644 --- a/README.ja.md +++ b/README.ja.md @@ -207,7 +207,7 @@ val mapper: KMapper = KMapper(Dst::class) #### デフォルトで利用可能な変換 いくつかの変換機能は、特別な記述無しに利用することができます。 -##### ネストしたマッピング +##### 1対1変換(ネストしたマッピング) 引数をそのまま用いることができず、かつその他の変換も行えない場合、`KMapper`は内部でマッピングクラスを用い、1対1マッピングを試みます。 これによって、デフォルトで以下のようなネストしたマッピングを行うことができます。 From d9c04318a2c17c855d50302b799cb3cbfa532b94 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Fri, 24 Jul 2020 01:17:27 +0900 Subject: [PATCH 25/33] =?UTF-8?q?=E3=81=9D=E3=81=AE=E4=BB=96=E3=81=AE?= =?UTF-8?q?=E5=A4=89=E6=8F=9B=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.ja.md b/README.ja.md index f535fc8..8861f88 100644 --- a/README.ja.md +++ b/README.ja.md @@ -228,6 +228,25 @@ println(dst.param) // -> InnerDst(foo=1, bar=2) ネストしたマッピングは、`KMapper`をクラスから指定した場合と同様に行われます。 このため、`KConstructor`アノテーションを用いて呼び出し対象を指定することができます。 +##### その他の変換 + +###### StringからEnumへの変換 +入力が`String`で、かつ引数が`Enum`だった場合、入力と対応する`name`を持つ`Enum`への変換が試みられます。 + +```kotlin +enum class FizzBuzz { + Fizz, Buzz, FizzBuzz; +} + +data class Dst(val fizzBuzz: FizzBuzz) + +val dst = KMapper(::Dst).map("fizzBuzz" to "Fizz") +println(dst) // -> Dst(fizzBuzz=Fizz) +``` + +###### Stringへの変換 +引数が`String`だった場合、入力を`toString`する変換が行われます。 + ### マッピング時に用いる引数名・フィールド名の設定 `KMapper`は、デフォルトでは引数名に対応する名前のフィールドをソースからそのまま探します。 一方、引数名とソースで違う名前を用いたいという場合も有ります。 From c05d222dae22d3bb7cafeaaca87c3b5ca942f4bf Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Fri, 24 Jul 2020 01:27:10 +0900 Subject: [PATCH 26/33] =?UTF-8?q?KConverter=E3=82=A2=E3=83=8E=E3=83=86?= =?UTF-8?q?=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E5=91=A8=E3=82=8A=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/README.ja.md b/README.ja.md index 8861f88..38d4ef4 100644 --- a/README.ja.md +++ b/README.ja.md @@ -247,6 +247,45 @@ println(dst) // -> Dst(fizzBuzz=Fizz) ###### Stringへの変換 引数が`String`だった場合、入力を`toString`する変換が行われます。 +#### KConverterアノテーションを設定することによる変換 +自作のクラスで、かつ単一引数から初期化できる場合、`KConverter`アノテーションを用いた変換が利用できます。 +`KConverter`アノテーションは、コンストラクタ、もしくは`companion object`に定義したファクトリーメソッドに対して付与できます。 + +```kotlin +// プライマリーコンストラクタに付与した場合 +data class FooId @KConverter constructor(val id: Int) +``` + +```kotlin +// セカンダリーコンストラクタに付与した場合 +data class FooId(val id: Int) { + @KConverter + constructor(id: String) : this(id.toInt()) +} +``` + +```kotlin +// ファクトリーメソッドに付与した場合 +data class FooId(val id: Int) { + companion object { + @KConverter + fun of(id: String): FooId = FooId(id.toInt()) + } +} +``` + +```kotlin +// fooIdにKConverterが付与されていればDstでは何もせずに正常にマッピングができる +data class Dst( + fooId: FooId, + bar: String, + baz: Int?, + + ... + +) +``` + ### マッピング時に用いる引数名・フィールド名の設定 `KMapper`は、デフォルトでは引数名に対応する名前のフィールドをソースからそのまま探します。 一方、引数名とソースで違う名前を用いたいという場合も有ります。 From 83918d0b07b9c8033d0f73d1cb7bcb288c09579d Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sun, 26 Jul 2020 03:16:16 +0900 Subject: [PATCH 27/33] =?UTF-8?q?=E3=82=B3=E3=83=B3=E3=83=90=E3=83=BC?= =?UTF-8?q?=E3=83=88=E3=82=A2=E3=83=8E=E3=83=86=E3=83=BC=E3=82=B7=E3=83=A7?= =?UTF-8?q?=E3=83=B3=E3=82=92=E5=AE=9A=E7=BE=A9=E3=81=99=E3=82=8B=E6=89=80?= =?UTF-8?q?=E3=81=BE=E3=81=A7=E8=A8=98=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.ja.md b/README.ja.md index 38d4ef4..b966e01 100644 --- a/README.ja.md +++ b/README.ja.md @@ -286,6 +286,28 @@ data class Dst( ) ``` +#### コンバートアノテーションを自作しての変換 +1対1の変換で`KConverter`を用いることができない場合、コンバートアノテーションを自作してパラメータに付与することで変換を行うことができます。 + +コンバートアノテーションの自作はコンバートアノテーションとコンバータの組を定義することで行います。 +例として`java.sql.Timestamp`もしくは`java.time.Instant`から指定したタイムゾーンの`ZonedDateTime`に変換を行う`ZonedDateTimeConverter`の作成の様子を示します。 + +##### コンバートアノテーションを定義する +`@Target(AnnotationTarget.VALUE_PARAMETER)`と`KConvertBy`アノテーション、他幾つかのアノテーションを付与することで、コンバートアノテーションを定義できます。 + +`KConvertBy`アノテーションの引数は、後述するコンバーターの`KClass`を渡します。 +このコンバーターはソースとなる型ごとに定義する必要があります。 + +また、この例ではアノテーションに引数を定義していますが、この値はコンバーターから参照することができます。 + +```kotlin +@Target(AnnotationTarget.VALUE_PARAMETER) +@Retention(AnnotationRetention.RUNTIME) +@MustBeDocumented +@KConvertBy([TimestampToZonedDateTimeConverter::class, InstantToZonedDateTimeConverter::class]) +annotation class ZonedDateTimeConverter(val zoneIdOf: String) +``` + ### マッピング時に用いる引数名・フィールド名の設定 `KMapper`は、デフォルトでは引数名に対応する名前のフィールドをソースからそのまま探します。 一方、引数名とソースで違う名前を用いたいという場合も有ります。 From e78cbfe4499b382a70b0d552ff2942f6a1f7bcba Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sun, 26 Jul 2020 03:24:58 +0900 Subject: [PATCH 28/33] =?UTF-8?q?=E5=A4=89=E6=8F=9B=E5=87=A6=E7=90=86?= =?UTF-8?q?=E5=85=A8=E4=BD=93=E3=82=92=E8=BF=BD=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/README.ja.md b/README.ja.md index b966e01..2cec57f 100644 --- a/README.ja.md +++ b/README.ja.md @@ -308,6 +308,75 @@ data class Dst( annotation class ZonedDateTimeConverter(val zoneIdOf: String) ``` +##### コンバーターを定義する +コンバーターは`AbstractKConverter`を継承して定義します。 +ジェネリクス`A`,`S`,`D`はそれぞれ以下の意味が有ります。 +- `A`: コンバートアノテーションの`Type` +- `S`: 変換前の`Type` +- `D`: 変換後の`Type` + +以下は`java.sql.Timestamp`から`ZonedDateTime`へ変換を行うコンバーターの例です。 + +```kotlin +class TimestampToZonedDateTimeConverter( + annotation: ZonedDateTimeConverter +) : AbstractKConverter(annotation) { + private val timeZone = ZoneId.of(annotation.zoneIdOf) + + override val srcClass: KClass = Timestamp::class + + override fun convert(source: Timestamp): ZonedDateTime = ZonedDateTime.of(source.toLocalDateTime(), timeZone) +} +``` + +コンバーターのプライマリコンストラクタの引数はコンバートアノテーションのみ取る必要が有ります。 +これは`KMapper`の初期化時に呼び出されます。 + +例の通り、アノテーションに定義した引数は適宜参照することができます。 + +##### 付与する +ここまでで定義したコンバートアノテーションとコンバーターをまとめて書くと以下のようになります。 +`InstantToZonedDateTimeConverter`は`java.time.Instant`をソースとするコンバーターです。 + +```kotlin +@Target(AnnotationTarget.VALUE_PARAMETER) +@Retention(AnnotationRetention.RUNTIME) +@MustBeDocumented +@KConvertBy([TimestampToZonedDateTimeConverter::class, InstantToZonedDateTimeConverter::class]) +annotation class ZonedDateTimeConverter(val zoneIdOf: String) + +class TimestampToZonedDateTimeConverter( + annotation: ZonedDateTimeConverter +) : AbstractKConverter(annotation) { + private val timeZone = ZoneId.of(annotation.zoneIdOf) + + override val srcClass: KClass = Timestamp::class + + override fun convert(source: Timestamp): ZonedDateTime = ZonedDateTime.of(source.toLocalDateTime(), timeZone) +} + +class InstantToZonedDateTimeConverter( + annotation: ZonedDateTimeConverter +) : AbstractKConverter(annotation) { + private val timeZone = ZoneId.of(annotation.zoneIdOf) + + override val srcClass: KClass = Instant::class + + override fun convert(source: Instant): ZonedDateTime = ZonedDateTime.ofInstant(source, timeZone) +} +``` + +これを付与すると以下のようになります。 + +```kotlin +data class Dst( + @ZonedDateTimeConverter("Asia/Tokyo") + val t1: ZonedDateTime, + @ZonedDateTimeConverter("-03:00") + val t2: ZonedDateTime +) +``` + ### マッピング時に用いる引数名・フィールド名の設定 `KMapper`は、デフォルトでは引数名に対応する名前のフィールドをソースからそのまま探します。 一方、引数名とソースで違う名前を用いたいという場合も有ります。 From f30d5ae0e5c962f3c5bb46f569686306738bf2e7 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sun, 26 Jul 2020 03:30:19 +0900 Subject: [PATCH 29/33] =?UTF-8?q?=E5=BE=AE=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.ja.md b/README.ja.md index 2cec57f..7cf969b 100644 --- a/README.ja.md +++ b/README.ja.md @@ -225,7 +225,7 @@ println(dst.param) // -> InnerDst(foo=1, bar=2) ``` ###### ネストしたマッピングに用いられる関数の指定 -ネストしたマッピングは、`KMapper`をクラスから指定した場合と同様に行われます。 +ネストしたマッピングは、`BoundKMapper`をクラスから初期化して用いることで行われます。 このため、`KConstructor`アノテーションを用いて呼び出し対象を指定することができます。 ##### その他の変換 From 3e98b8bb232ae990513d68fac8788ee9bd644446 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sun, 26 Jul 2020 03:52:33 +0900 Subject: [PATCH 30/33] =?UTF-8?q?KParameterFlatten=E5=91=A8=E3=82=8A?= =?UTF-8?q?=E3=81=AE=E8=BF=BD=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/README.ja.md b/README.ja.md index 7cf969b..80c0034 100644 --- a/README.ja.md +++ b/README.ja.md @@ -377,6 +377,74 @@ data class Dst( ) ``` +#### 複数引数からの変換 +以下のような`Dst`で、`InnerDst`をマップ元の複数のフィールドから変換したい場合、`KParameterFlatten`アノテーションが利用できます。 + +```kotlin +data class InnerDst(val fooFoo: Int, val barBar: String) +data class Dst(val bazBaz: InnerDst, val quxQux: LocalDateTime) +``` + +`Dst`のフィールド名をプレフィックスに指定する場合以下のように付与します。 +ここで、`KParameterFlatten`を指定されたクラスは、前述の`KConstructor`アノテーションで指定した関数またはプライマリコンストラクタから初期化されます。 + +```kotlin +data class InnerDst(val fooFoo: Int, val barBar: String) +data class Dst( + @KParameterFlatten + val bazBaz: InnerDst, + val quxQux: LocalDateTime +) +data class Src(val bazBazFooBoo: Int, val bazBazBarBar: String, val quxQux: LocalDateTime) + +// bazBazFooFoo, bazBazBarBar, quxQuxの3引数が要求される +val mapper = KMapper(::Dst) +``` + +##### KParameterFlattenアノテーションのオプション +`KParameterFlatten`アノテーションはネストしたクラスの引数名の扱いについて2つのオプションを持ちます。 + +###### fieldNameToPrefix +`KParameterFlatten`アノテーションはデフォルトでは引数名をプレフィックスに置いた名前で一致を見ようとします。 +引数名をプレフィックスに付けたくない場合は`fieldNameToPrefix`オプションに`false`を指定します。 + +```kotlin +data class InnerDst(val fooFoo: Int, val barBar: String) +data class Dst( + @KParameterFlatten(fieldNameToPrefix = false) + val bazBaz: InnerDst, + val quxQux: LocalDateTime +) + +// fooFoo, barBar, quxQuxの3引数が要求される +val mapper = KMapper(::Dst) +``` + +`fieldNameToPrefix = false`を指定した場合、`nameJoiner`オプションは無視されます。 + +###### nameJoiner +`nameJoiner`は引数名と引数名の結合方法の指定です。 +例えば`Src`が`snake_case`だった場合、以下のように利用します。 + +```kotlin +data class InnerDst(val fooFoo: Int, val barBar: String) +data class Dst( + @KParameterFlatten(nameJoiner = NameJoiner.Snake::class) + val bazBaz: InnerDst, + val quxQux: LocalDateTime +) + +// baz_baz_foo_foo, baz_baz_bar_bar, qux_quxの3引数が要求される +val mapper = KMapper(::Dst) { /* キャメル -> スネークの命名変換関数 */ } +``` + +デフォルトでは`camelCase`が指定されており、`snake_case`と`kebab-case`のサポートも有ります。 +`NameJoiner`クラスを継承した`object`を作成することで自作することもできます。 + +##### 他の変換方法との併用 +`KParameterFlatten`アノテーションを付与した場合も、これまでに紹介した変換方法は全て機能します。 +また、`KParameterFlatten`アノテーションは何重にネストした中でも利用が可能です。 + ### マッピング時に用いる引数名・フィールド名の設定 `KMapper`は、デフォルトでは引数名に対応する名前のフィールドをソースからそのまま探します。 一方、引数名とソースで違う名前を用いたいという場合も有ります。 From 3c3a99652c964c86ce4637d656a7c9b70d7583e8 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sun, 26 Jul 2020 03:56:10 +0900 Subject: [PATCH 31/33] =?UTF-8?q?=E5=BC=95=E6=95=B0=E5=90=8D=E3=81=AE?= =?UTF-8?q?=E5=A4=89=E6=8F=9B=E5=87=A6=E7=90=86=E3=81=AE=E4=BC=9D=E6=92=AD?= =?UTF-8?q?=E3=81=AB=E3=81=A4=E3=81=84=E3=81=A6=E8=BF=BD=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.ja.md b/README.ja.md index 80c0034..7434159 100644 --- a/README.ja.md +++ b/README.ja.md @@ -476,6 +476,10 @@ val dst = mapper.map(mapOf( また、当然ながらラムダ内で任意の変換処理を行うこともできます。 +##### 引数名の変換処理の伝播について +引数名の変換処理は、ネストしたマッピングにも反映されます。 +また、後述する`KParameterAlias`アノテーションで指定したエイリアスに関しても変換が適用されます。 + #### 実際の変換処理 `KMapper`では命名変換処理を提供していませんが、プロジェクトでよく用いられるライブラリでも命名変換処理が提供されている場合が有ります。 `Jackson`、`Guava`の2つのライブラリで実際に「キャメルケース -> スネークケース」の変換処理を渡すサンプルコードを示します。 From 8948e929b5567cd44e66c9ffc1105c5ad5d3007e Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sun, 26 Jul 2020 03:58:52 +0900 Subject: [PATCH 32/33] =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.ja.md b/README.ja.md index 7434159..d028167 100644 --- a/README.ja.md +++ b/README.ja.md @@ -34,7 +34,6 @@ val dst = KMapper(::Dst).map(src) ソースは1つに限らず、複数のオブジェクトや、`Pair`、`Map`等を指定することもできます。 ```kotlin -"" val dst = KMapper(::Dst).map( "param1" to "value of param1", mapOf("param2" to 1, "param3" to 2L), From dfb4a97ab992736761cda59d4ed40fb6fd2adceb Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sun, 26 Jul 2020 04:07:07 +0900 Subject: [PATCH 33/33] =?UTF-8?q?KGetterIgnore=E5=91=A8=E3=82=8A=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.ja.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.ja.md b/README.ja.md index d028167..9e9316e 100644 --- a/README.ja.md +++ b/README.ja.md @@ -563,6 +563,18 @@ class Foo( ) ``` +#### マッピング時にフィールドを無視する +何らかの理由でマッピング時にフィールドを無視したい場合、`KGetterIgnore`アノテーションを用いることができます。 +例えば、以下の`Src`クラスを入力した場合、`param1`フィールドは読み出し処理が行われません。 + +```kotlin +data class Src( + @KGetterIgnore + val param1: Int, + val param2: Int +) +``` + ## 引数のセットアップ ### 引数読み出しの対象