Kotlinではdata class
:
data class CountriesResponse(
val count: Int,
val countries: List<Country>,
val error: String)
次に、それを使用してJSONを解析できます(例: "{n:10}")。この場合、オブジェクトval countries: CountriesResponse
、これらの値を含むRetrofit
、Fuel
またはGson
から受信:count = 0, countries = null, error = null
。
Kotlin + Gson-データクラスに対してnullの場合にemptyListを取得する方法 には、別の例があります。
後でcountries
を使用しようとすると、ここで例外が発生します:val size = countries.countries.size
: "kotlin.TypeCastException:nullをnull以外の型kotlin.Intにキャストすることはできません。"コードを記述して?
これらのフィールドにアクセスすると、Android Studioは?.
と警告:Unnecessary safe call on a non-null receiver of type List<Country>
。
したがって、?
データクラス内?実行時にアプリケーションがnull
をnullにできない変数に設定できるのはなぜですか?
これは、Gsonが安全でない(Java.misc.Unsafe
のような)インスタンス構築メカニズムを使用してクラスのインスタンスを作成し、コンストラクタをバイパスして、フィールドを直接設定するために発生します。
いくつかの調査については、このQ&Aを参照してください: Got Desrialization with Kotlin、Initializerブロックが呼び出されていません 。
結果として、Gsonはコンストラクションロジックとクラス状態の不変条件の両方を無視するため、これによって影響を受ける可能性のある複雑なクラスに使用することは推奨されません。セッターの値チェックも無視します。
Jackson(上記のQ&Aで説明)や kotlinx.serialization
などのKotlin対応のシリアル化ソリューションを検討してください。
これは、Gsonを含む多くのJsonパーサーが、存在しないフィールドを静かに飲み込むようにするという設計上の決定です。オブジェクトはnull
に設定され、プリミティブは0
に設定されています。これにより、APIエラーが完全にマスクされ、さらに下流で暗号エラーが発生します。
このため、2段階の解析をお勧めします。
package com.example.transport
//this class is passed to Gson (or any other parser)
data class CountriesResponseTransport(
val count: Int?,
val countries: List<CountryTransport>?,
val error: String?){
fun toDomain() = CountriesResponse(
count ?: throw MandatoryIsNullException("count"),
countries?.map{it.toDomain()} ?: throw MandatoryIsNullException("countries"),
error ?: throw MandatoryIsNullException("error")
)
}
package com.example.domain
//this one is actually used in the app
data class CountriesResponse(
val count: Int,
val countries: Collection<Country>,
val error: String)
はい、2倍の労力がかかりますが、APIエラーをすぐに特定し、修正できない場合にそれらのエラーを処理する場所を提供します。
fun toDomain() = CountriesResponse(
count ?: countries?.count ?: -1, //just to brag we can default to non-zero
countries?.map{it.toDomain()} ?: ArrayList()
error ?: MyApplication.INSTANCE.getDeafultErrorMessage()
)
はい、より多くのオプションを使用して、より優れたパーサーを使用できますが、使用しないでください。あなたがしなければならないのは、パーサーを抽象化して、どれでも使えるようにすることです。今日、どれほど高度で構成可能なパーサーを見つけても、結局はそれがサポートしない機能が必要になるでしょう。そのため、私はGsonを最小公分母として扱います。
記事があります リポジトリパターンのより大きなコンテキストで使用(および拡張)されたこの概念を説明します。