使用Moshi反序列化可以是两种数据类型之一的字段

时间:2017-09-06 15:13:25

标签: java android json kotlin moshi

我从OrientDB服务器收到一些看起来像这样的JSON:

{
    ...
    "out": ...,
    "in": ...,
    ...
}

现在,这两个字段outin可以是以下两种类型之一:String和我自己的自定义对象(让我们将其称为Record) 。例如,对于一个请求,我可能会收到:

{
    ...
    "out": "#17:0",
    "in": {
        ...
    },
    ...
}

另一个我可能会得到:

{
    ...
    "out": {
        ...
    },
    "in": "#18:2",
    ...
}

等等。两者都可能是String,两者都可能是Records,一个可能是String,另一个可能是Record,等等。现在,当我使用Moshi对这种JSON进行反序列化时,我有两个参数outin来保存各自键的值;但是,因为这些值不是固定的数据类型,所以说起来容易做起来难。

创建多个POJO(或者#34; POKO" s,我猜,因为我使用的是Kotlin)不会起作用,因为这些对象可以在其他中找到em> JSON对象和类似的东西。我需要一个对象,这些参数可以采用可变数据类型。那我该怎么做呢?

我是否必须在Moshi中编写自定义适配器来序列化/反序列化这些值?如果是这样,我将如何编写一个可以根据参数值分配某种数据类型?或者是否有某种Kotlin类/函数/扩展函数我可以找到/写入可以容纳两种可能的数据类型?

如果它是相关的,我也使用Retrofit 2 + RxJava 2来异步进行HTTP调用,所以如果这些库中有任何数据类型或函数可以促进这样的事情我全都听见了。

即使有人只能用Java回答也没关系,因为我可以自己转换代码。如果我遗漏了一些明显的东西,我会事先道歉。

1 个答案:

答案 0 :(得分:0)

您可以执行类似此答案中的操作:https://stackoverflow.com/a/65106419/3543610

基本上,您将创建一个密封类作为属性inout的类型。您还需要将原始字符串一个包装成一个包含String的类型,以便可以使其扩展密封的类,如下所示:

sealed class YourType {
    data class StringData(val value: String) : YourType()

    @JsonClass(generateAdapter = true)
    data class Record(
        val prop1: String,
        val prop2: Int
    ) : YourType()
}

然后具有这些属性的模型看起来像:

@JsonClass(generateAdapter = true)
data class Model(
    ...
    val in: YourType,
    val out: YourType,
    ...
)

然后,您最终为YourType类型编写自定义适配器:

class YourTypeCustomAdapter {
    @FromJson
    fun fromJson(jsonReader: JsonReader, delegate: JsonAdapter<Record>): YourType? {
        return if (jsonReader.peek() == BEGIN_OBJECT) {
            delegate.fromJson(jsonReader)
        } else {
            StringData(jsonReader.nextString())
        }
    }

    @ToJson
    fun toJson(jsonWriter: JsonWriter, yourType: YourType, delegate: JsonAdapter<Record>) {
        when (yourType) {
            is Record -> delegate.toJson(jsonWriter, yourType)
            is StringData -> jsonWriter.value(yourType.value)
        }
    }
}

并向Moshi注册:

private val moshi = Moshi.Builder()
    .add(YourTypeCustomAdapter())
    .build()