ConversionService
ConversionService は、ある値を別の型の値に変えます。たとえば設定ファイルのテキスト '10s' を Duration にします。
使う理由
フレームワークは型付きの値を埋めるたびに変換サービスを呼びます。@Value の注入、PropertyResolver.getPropertyAs<T>、@ConfigurationProperties のバインドです。DefaultConversionService は一般的な型をすでに知っているため、ほとんどのアプリケーションは結果を受け取るだけです。
自分で使うのは次の 2 つの場合です。
- 独自の型。 その型のコンバーターを持つ
conversionServiceBean を宣言すると、@Valueでその型を注入できます。この Bean は祖先のコンテキストにあっても見つかります。 - enum。 リフレクションがないため、Dart には値の一覧が必要です。
addEnum(MyEnum.values)で登録します。
自分のコードで値を変換するときも convert<T> を直接呼びます。
DefaultConversionService に組み込まれている変換:
Stringからint(16 進の0x1F、#1Fも可)、double、num、BigInt、bool、Uri、DateTime(ISO-8601)、Duration(10s、PT10S、単位なしはミリ秒)、DataSize(10MB)、RegExp、Resource、Encoding。これらの多くでは、空白だけの文字列はnullになります。boolはtrue、on、yes、1とfalse、off、no、0を大文字小文字を問わず受け付けます。それ以外は失敗します。intからDuration(ミリ秒)とDataSize(バイト)。num、int、double、BigInt間の変換。- あらゆる値から
String。enum は名前、リストはa,b、DurationとDateTimeは ISO-8601 になります。 String、int、double、num、bool、BigInt、Duration、DataSize、UriのListとSet。カンマ区切りの文字列、Iterable、単一の値から変換します。- それらの要素型と
Object?のMap<String, V>。
例
import 'package:spring_core_dart/spring_core_dart.dart';
/// 金額。設定ファイルでは `12.50 EUR` と書く。
class Money {
const Money(this.amount, this.currency);
final double amount;
final String currency;
static Money parse(String text) {
final [amount, currency] = text.trim().split(' ');
return Money(double.parse(amount), currency);
}
}
enum Level { info, warn, error }
@Configuration()
class ConversionConfig {
// コンテキストはこの Bean で @Value の引数を変換するので、
// `@Value(r'${shop.min-order}') Money minOrder` が使える。
@Bean()
ConversionService conversionService() => DefaultConversionService()
..addConverter<String, Money>(Money.parse)
..addCollectionConverters<Money>() // List<Money> と Set<Money>
..addEnum(Level.values); // `warn`、`WARN`、`Warn` のどれでも一致する
}
@Service()
class Checkout {
Checkout(
@Value(r'${shop.min-order:10.00 EUR}') this.minOrder,
@Value(r'${shop.alert-level:warn}') this.alertLevel,
);
final Money minOrder;
final Level alertLevel;
}
コードでの変換:
final service = DefaultConversionService();
service.convert<int>('0x1F'); // 31
service.convert<bool>('yes'); // true
service.convert<List<int>>('1, 2, 3'); // [1, 2, 3]
service.convert<int?>(''); // null
service.convert<String>(const Duration(seconds: 10)); // 'PT10S'
エラー:
ConversionFailedException: コンバーターはあるが値を解析できない、または null 非許容の型に対して結果がnullになった。ConverterNotFoundException: その型を対象とするコンバーターがない。
メンバー
ConversionService:
| 名前 | 戻り値 | 意味 |
|---|---|---|
convert<T>(source) |
T |
T に変換する。すでに T ならそのまま返す。 |
convertTo(source, targetType) |
Object? |
実行時に Type 値としてだけ分かる型に変換する。 |
canConvert(source, targetType) |
bool |
この値を受け付けるコンバーターがあるか。値が解析できるかは確認しない。 |
canConvertType<S>(targetType) |
bool |
値がまだない段階で、型 S の値を変換できるか。 |
ConverterRegistry(GenericConversionService と DefaultConversionService が実装):
| 名前 | 意味 |
|---|---|
addConverter<S, T>(converter) |
S から T ちょうどへの関数を追加する(T? にも使われる)。同じ型への後の登録が優先される。 |
addEnum<E>(values) |
enum を String との間で、また List<E> / Set<E> として変換できるようにする。名前は大文字小文字と区切り文字を無視して照合する。 |
addCollectionConverters<E>() |
List<E> と Set<E> を変換できるようにする。 |
addMapConverters<K, V>() |
任意の Map から Map<K, V> に変換できるようにする。 |
removeConvertible<T>() |
T と T? を対象とするコンバーターをすべて削除する。 |
DefaultConversionService.sharedInstance は、サービスを与えられていないものすべてが使います。これにコンバーターを追加せず、自分のインスタンスを作ってください。
Spring (Java) との対応
@Bean
ConversionService conversionService() {
DefaultConversionService service = new DefaultConversionService();
service.addConverter(String.class, Money.class, Money::parse);
return service;
}
Integer port = service.convert("8080", Integer.class);
重要な違い:
- 変換先は Dart の
Typeの完全一致で照合します。List<int>とList<String>は別の変換先で、要素型ごとにaddCollectionConverters<E>()が必要です。Spring はジェネリックな変換先にTypeDescriptorを使います。 - enum は
addEnumで登録する必要があります。Spring は登録なしですべての enum を変換します。 - コンバーターはただの関数です。実装すべき
Converterインターフェースはありません。 canConvertはソースの値を調べます。canConvertType<S>が Spring のcanConvert(Class, Class)に当たります。DurationとDataSizeの解析はDefaultConversionServiceに組み込まれています。Spring では Spring Boot のApplicationConversionServiceにあります。