Вы можете использовать JsonAdapter из moshi для анализа различных моделей JSON, если вы можете дифференцировать их, предвидя какое-то значение в json.
Например, рассмотрите ответ json, имеющий две схемы,
{
"root": {
"subroot": {
"prop" : "hello",
"type" : "String"
}
}
}
(or)
{
"root": {
"subroot": {
"prop" : 100,
"type" : "Integer"
}
}
}
Здесь subroot имеет разные схемы (одна, содержащая строковое свойство, а другая, содержащая целочисленное свойство), которые можно идентифицировать с помощью «типа»
Вы можете создать родительский запечатанный класс с общими ключами и получить несколько дочерних.классы с разными ключами.Напишите адаптер, чтобы выбрать тип класса, который будет использоваться при сериализации json, и добавьте этот адаптер в moshi builder.
Классы моделей:
class Response {
@Json(name = "root")
val root: Root? = null
}
class Root {
@Json(name = "subroot")
val subroot: HybridModel? = null
}
sealed class HybridModel {
@Json(name = "type")
val type: String? = null
class StringModel : HybridModel() {
@Json(name = "prop")
val prop: String? = null
}
class IntegerModel : HybridModel() {
@Json(name = "prop")
val prop: Int? = null
}
}
Несколько методов расширения для JsonReader,
inline fun JsonReader.readObject(process: () -> Unit) {
beginObject()
while (hasNext()) {
process()
}
endObject()
}
fun JsonReader.skipNameAndValue() {
skipName()
skipValue()
}
HybridAdapter для выбора типа класса для ключа "subroot"
class HybridAdapter : JsonAdapter<HybridModel>() {
@FromJson
override fun fromJson(reader: JsonReader): HybridModel {
var type: String = ""
// copy reader and foresee type
val copy = reader.peekJson()
copy.readObject {
when (copy.selectName(JsonReader.Options.of("type"))) {
0 -> {
type = copy.nextString()
}
else -> copy.skipNameAndValue()
}
}
//handle exception if type cannot be identified
if (type.isEmpty()) throw JsonDataException("missing type")
// build model based on type
val moshi = Moshi.Builder().build()
return if (type == "String")
moshi.adapter(HybridModel.StringModel::class.java).fromJson(reader)!!
else
moshi.adapter(HybridModel.IntegerModel::class.java).fromJson(reader)!!
}
@ToJson
override fun toJson(p0: JsonWriter, p1: HybridModel?) {
// serialization logic
}
}
Наконец, создайте Moshi с HybridAdapter для сериализации HybridModel,
fun printProp(response: Response?) {
val subroot = response?.root?.subroot
when (subroot) {
is HybridModel.StringModel -> println("string model: ${subroot.prop}")
is HybridModel.IntegerModel -> println("Integer model: ${subroot.prop}")
}
}
fun main() {
val jsonWithStringSubroot =
"""
{
"root": {
"subroot": {
"prop" : "hello",
"type" : "String"
}
}
}
"""
val jsonWithIntegerSubroot =
"""
{
"root": {
"subroot": {
"prop" : 1,
"type" : "Integer"
}
}
}
"""
val moshi = Moshi.Builder().add(HybridAdapter()).build()
val response1 = moshi.adapter(Response::class.java).fromJson(jsonWithStringSubroot)
printProp(response1) // contains HybridModel.StringModel
val response2 = moshi.adapter(Response::class.java).fromJson(jsonWithIntegerSubroot)
printProp(response2) // contains HybridModel.IntegerModel
}