DataConversion
feature を用いると値のリストのシリアライズ方法およびデシリアライズ方法を定義することができます。
プリミティブ型や列挙型の変換は予め定義されていますが、 DataConversion
を用いることで、他の型も処理できるようになります。
Locations feature を使用していて、かつパラメータの一部をプリミティブ型や列挙型以外の 型に変換したい場合、この feature を用いて自作のコンバータを作成することができます。
目次:
io.ktor.features.DataConversion
and no additional artifacts are required.
DataConversion のインストールは簡単です。 プリミティブ型の変換はデフォルトで対応済です。
install(DataConversion)
DataConversion は、型変換を定義するためのメソッド convert<T>
を提供します。
このメソッド内で、デコード処理を行う decode
コールバック関数と
エンコード処理を行う encode
コールバック関数を定義します。
converter: (values: List<String>, type: Type) -> Any?
values
と Type 型の type
を引数に取ります。values
は URL 内で繰り返し使用されうるキーの値です。 (例 : a=1&a=b
)type
はコンバート先の型を指定します。converter: (value: Any?) -> List<String>
key=item1
のようにシリアライズされます。samekey=item1&samekey=item2
のようなクエリ文字列にシリアライズされます。例:
install(DataConversion) {
convert<Date> { // this: DelegatingConversionService
val format = SimpleDateFormat.getInstance()
decode { values, _ -> // converter: (values: List<String>, type: Type) -> Any?
values.singleOrNull()?.let { format.parse(it) }
}
encode { value -> // converter: (value: Any?) -> List<String>
when (value) {
null -> listOf()
is Date -> listOf(SimpleDateFormat.getInstance().format(value))
else -> throw DataConversionException("Cannot convert $value as Date")
}
}
}
}
他の用途の一つとして、列挙型の変換方法を独自定義することが挙げられます。
デフォルトでは大文字と小文字は区別され、 .name
を用いてシリアライズ / デシリアライズされます。
一方で、例えばシリアライズ時は小文字で出力し、デシリアライズ時は大文字と小文字を区別したくない場合は、下記のように実装します。
enum class LocationEnum {
A, B, C
}
@Location("/") class LocationWithEnum(val e: LocationEnum)
@Test fun `location class with custom enum value`() = withLocationsApplication {
application.install(DataConversion) {
convert(LocationEnum::class) {
encode { if (it == null) emptyList() else listOf((it as LocationEnum).name.toLowerCase()) }
decode { values, type -> LocationEnum.values().first { it.name.toLowerCase() in values } }
}
}
application.routing {
get<LocationWithEnum> {
call.respondText(call.locations.resolve<LocationWithEnum>(LocationWithEnum::class, call).e.name)
}
}
urlShouldBeHandled("/?e=a", "A")
urlShouldBeHandled("/?e=b", "B")
}
下記のように、 DataConversion service は簡単に参照できます。
val dataConversion = call.conversionService
interface ConversionService {
fun fromValues(values: List<String>, type: Type): Any?
fun toValues(value: Any?): List<String>
}
class DelegatingConversionService(private val klass: KClass<*>) : ConversionService {
fun decode(converter: (values: List<String>, type: Type) -> Any?)
fun encode(converter: (value: Any?) -> List<String>)
}