eri*_*cky 0 gson kotlin arrow-kt
我在 Kotlin 中有一个简单的书籍和借书人图书馆的模型,如果有借书人,就会借出一本书。我使用 Arrow Option 对借款人的缺席/存在进行编码:
data class Borrower(val name: Name, val maxBooks: MaxBooks)
data class Book(val title: String, val author: String, val borrower: Option<Borrower> = None)
Run Code Online (Sandbox Code Playgroud)
我在 Gson 中将这些对象序列化/反序列化为 JSON 时遇到问题 - 特别是书籍中Option<Borrower>JSON的表示null:
[
{
"title": "Book100",
"author": "Author100",
"borrower": {
"name": "Borrower100",
"maxBooks": 100
}
},
{
"title": "Book200",
"author": "Author200",
"borrower": null
}
]
Run Code Online (Sandbox Code Playgroud)
我的反序列化代码:
fun jsonStringToBooks(jsonString: String): List<Book> {
val gson = Gson()
return try {
gson.fromJson(jsonString, object : TypeToken<List<Book>>() {}.type)
} catch (e: Exception) {
emptyList()
}
}
Run Code Online (Sandbox Code Playgroud)
我得到一个空列表。几乎相同的jsonStringToBorrowers工作正常。
有人可以指出我正确的方向吗?
使用不同的 JSON 库会喜欢kotlinx.serialization还是Klaxon更好的主意,他们是如何做null <-> None的?
谢谢!
由于您在返回空列表之前没有记录异常,因此该问题有点隐藏。如果你记录了那个异常,你会得到这个:
java.lang.RuntimeException:无法调用没有参数的私有 arrow.core.Option()
这意味着 Gson 不知道如何创建一个Option类,因为它没有公共的空构造函数。实际上,Option是一个密封类(因此是抽象类),具有 2 个具体的子类:Some和None。为了获得Option您的实例,您应该使用工厂方法之一,例如Option.just(xxx)或Option.empty()其中之一。
现在,为了修复您的代码,您需要告诉 Gson 如何反序列化一个Option类。为此,您需要为您的gson对象注册一个类型适配器。
一个可能的实现如下:
class OptionTypeAdapter<E>(private val adapter: TypeAdapter<E>) : TypeAdapter<Option<E>>() {
@Throws(IOException::class)
override fun write(out: JsonWriter, value: Option<E>) {
when (value) {
is Some -> adapter.write(out, value.t)
is None -> out.nullValue()
}
}
@Throws(IOException::class)
override fun read(input: JsonReader): Option<E> {
val peek = input.peek()
return if (peek != JsonToken.NULL) {
Option.just(adapter.read(input))
} else {
input.nextNull()
Option.empty()
}
}
companion object {
fun getFactory() = object : TypeAdapterFactory {
override fun <T> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
val rawType = type.rawType as Class<*>
if (rawType != Option::class.java) {
return null
}
val parameterizedType = type.type as ParameterizedType
val actualType = parameterizedType.actualTypeArguments[0]
val adapter = gson.getAdapter(TypeToken.get(actualType))
return OptionTypeAdapter(adapter) as TypeAdapter<T>
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
您可以通过以下方式使用它:
fun main(args: Array<String>) {
val gson = GsonBuilder()
.registerTypeAdapterFactory(OptionTypeAdapter.getFactory())
.create()
val result: List<Book> = try {
gson.fromJson(json, TypeToken.getParameterized(List::class.java, Book::class.java).type)
} catch (e: Exception) {
e.printStackTrace()
emptyList()
}
println(result)
}
Run Code Online (Sandbox Code Playgroud)
该代码输出:
[Book(title=Book100, author=Author100, borrower=Some(Borrower(name=Borrower100, maxBooks=100))), Book(title=Book200, author=Author200, borrower=None)]
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
561 次 |
| 最近记录: |