Невозможно создать конвертер для класса при использовании закрытого класса или интерфейса с Moshi - PullRequest
0 голосов
/ 04 июля 2019

Я пытаюсь проанализировать данные json с сервера. У него есть динамические ключи, поэтому я пытаюсь создать как родительский класс, имеющий общие ключи и дочерний класс для каждого конкретного узла.Я написал код kotlin, используя модернизацию и Moshi, но он не работает.Я попытался с запечатанным классом и интерфейсом без успеха.На самом деле я бы предпочел, чтобы это работало с закрытым классом, но я не знаю, что я делаю неправильно

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 Ответ

1 голос
/ 05 июля 2019

Вы можете использовать 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
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...