使用密封类或与Moshi的接口时,无法为类创建转换器

时间:2019-07-04 11:37:14

标签: json kotlin retrofit

我正在尝试从服务器解析json数据,它具有动态密钥,因此我试图像父类一样为每个特定节点具有共享密钥和子类。我使用翻新和Moshi编写了kotlin代码,但无法正常工作。我尝试使用密封的类和接口没有成功。实际上,我更喜欢在密封类中使用,但我不知道自己在做什么错事

interface MyApi {

    @GET("/...")
    fun fetchMyFeed(): Call<MyResponse>

}

data class MyResponse(
    val data: List<ParentResponse>
)
interface ParentResponse{
    val name: String
}

data class Child1Response(
    val age: String,
    val kids: List<KidsResponse>,
    val cars: List<CarsResponse>
)

data class Child2Response(
    val job: String,
    val address: List<AddressResponse>
)


fun fetchAllFeed(): List<Any>? =
        try {
            val response = api.fetchMyFeed().execute()
            if (response.isSuccessful) {
                Log.d("check",${response.body()?.data?})
                null
            } else null
        } catch (e: IOException) {
            null
        } catch (e: RuntimeException) {
            null
        }```

and the json file is : 

{
  "data": [
    {
      "name": "string",
      "job": "string",
      "address": [
        {
          "avenue": "string",
          "imageUrl": "string",
          "description": "string"
        }
      ]
    },
    {
      "name": "string",
      "age": "string",
      "kids": {
        "count": "string",
        "working": "string"
      },
      "cars": [
        {
          "brand": "string",
          "age": "string",
          "imageUrl": "string"
        }
      ]
    }
  ]
}

Unable to create converter for class

1 个答案:

答案 0 :(得分:1)

如果可以通过预知JSON中的某些值来区分它们,则可以使用moshi的JsonAdapter来解析不同的JSON模型。

例如,考虑具有两个模式的json响应,

{
  "root": {
      "subroot": {
         "prop" : "hello",
         "type" : "String"
       }
   }
}

(or)

{
  "root": {
      "subroot": {
         "prop" : 100,
         "type" : "Integer"
       }
   }
}

在这里,子根目录具有不同的模式(一个包含字符串属性,另一个包含整数属性),可以通过“类型”来标识

您可以创建具有公共密钥的父级密封类,并派生少量具有不同密钥的子类。编写一个适配器以选择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
    }
}

最后使用HybridAdapter构建Moshi来序列化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
}