Переходный с Kotlin? - PullRequest
0 голосов
/ 03 мая 2019

Попытка отобразить действительно плохо спроектированный JSON и столкнуться с проблемами.Массив внутри массива не нужен для моих целей, поэтому я пытаюсь исключить его из моего ответа в надежде, что он перестанет выдавать ошибки.

Уродливый JSON

{
    "id": "65",
    "name": "Switch - Kitchen",
    "label": "Switch - Kitchen",
    "attributes": [
        {
            "name": "switch",
            "currentValue": "off",
            "dataType": "ENUM",
            "values": [
                "on",
                "off"
            ]
        }
    ],
    "capabilities": [
        "Switch",
        {
            "attributes": [
                {
                    "name": "switch",
                    "dataType": null
                }
            ]
        },
        "Configuration",
        "Refresh",
        "Actuator"
    ],
    "commands": [
        "configure",
        "flash",
        "off",
        "on",
        "refresh",
        "refresh"
    ]
}

DeviceDetails Class

    @Transient val attributes: List<Attribute>,
    val capabilities: List<String>,
    val commands: List<String>,
    val id: String,
    val label: String,
    val name: String
)

Класс атрибута (не требуется)

data class Attribute(
    val currentValue: String,
    val dataType: String,
    val name: String,
    val values: List<String>
)

Подробности API

interface DeviceDetailsAPI {
    @GET("devices/65")
    fun getAllDetails(@Query("access_token") access_token: String): Observable<List<DeviceDetails>>
}

Вызов API

object DeviceDetailsWebAccess {
    val deviceDetailsApi: DeviceDetailsAPIClient by lazy {
        Log.d("DeviceDetailsWebAccess", "Creating details retrofit client")
        //Debugging URL//
        val interceptor : HttpLoggingInterceptor = HttpLoggingInterceptor().apply {
            this.level = HttpLoggingInterceptor.Level.BODY }
        val client : OkHttpClient = OkHttpClient.Builder().apply {
            this.addInterceptor(interceptor)}.build()
        //Debugging URL//
        val retrofit = Retrofit.Builder()
            .baseUrl("http://xxx.xxx.xxx.xxx/apps/api/109/")
            // Moshi maps JSON to classes
            .addConverterFactory(MoshiConverterFactory.create())
            // The call adapter handles threads
            .addCallAdapterFactory(CoroutineCallAdapterFactory())
            .client(client)
            .build()

        // Create Retrofit client
        return@lazy retrofit.create(DeviceDetailsAPIClient::class.java)
    }
}

Адаптер

class DeviceDetailsAdapter (var deviceDetails: List<DeviceDetails>, val clickListener: (DeviceDetails) -> Unit) :
    RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        val view = inflater.inflate(R.layout.device_detail, parent, false)
        return PartViewHolder(view)
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
    }

    override fun getItemCount() = deviceDetails.size

    class PartViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        fun bind(device: DeviceList) {
            itemView.device_item_name.text = device.name
            itemView.device_item_id.text = device.id
            itemView.device_item_label.text = device.label
        }
    }
}

Основная активность

class MainActivity2 : AppCompatActivity() {
    private val tag : String = MainActivity2::class.java.simpleName
    private lateinit var adapterDetails: DeviceDetailsAdapter
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        rv_devices.layoutManager = LinearLayoutManager(this)
        rv_devices.hasFixedSize()
        adapterDetails = DeviceDetailsAdapter(listOf()) { deviceDetails: DeviceDetails -> deviceDetails }
        rv_devices.adapter = adapterDetails
        loadDeviceDetails()
    }

        private fun loadDeviceDetails() {
            GlobalScope.launch(Dispatchers.Main) {
                try {
                    val webResponseDetails = DeviceDetailsWebAccess.deviceDetailsApi.getDevicesDetailsAsync(access_token = "xxxxxxxx").await()
                    if (webResponseDetails.isSuccessful) {
                        val deviceDetails: List<DeviceDetails>? = webResponseDetails.body()
                        //Log.d(tag, deviceDetails?.toString())
                        //adapterDetails.deviceDetails = deviceDetails ?: listOf()
                        adapterDetails.notifyDataSetChanged()
                    } else {
                        Log.e(tag, "Error ${webResponseDetails.code()}")
                        Toast.makeText(this@MainActivity2, "Error ${webResponseDetails.code()}", Toast.LENGTH_LONG).show()
                    }
                } catch (e: IOException) {
                    Log.e(tag, "Exception " + e.printStackTrace())
                    Toast.makeText(this@MainActivity2, "Exception ${e.message}", Toast.LENGTH_LONG).show()
                }
            }
        }
    }

Я получаю следующую ошибку:

com.squareup.moshi.JsonDataException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at path $

Не указывается, где проблема, которую я нахожу странной?Я отслеживаю вызов JSON, и он показывает, что массив «Атрибут» все еще анализируется.Что я делаю неправильно?Почему Transient не работает?Любая помощь приветствуется.

Ответы [ 2 ]

2 голосов
/ 03 мая 2019

Ошибка говорит о том, что проблема существует в path $, который является корнем.

Ваша проблема в том, что вы говорите, что JSON - это List<DeviceDetails>, но, похоже, на самом деле это всего лишь одинDeviceDetails.

0 голосов
/ 05 мая 2019

Как ответил @Kiskae, класс ожидает список DeviceDetails, и текущий JSON будет объектом DeviceDetails.

Обычно список в классе модели соответствует синтаксису массива в JSON [ {object 1}, {object 2} ]

Ваш корневой JSON является объектом. Чтобы изменить его, чтобы вы могли проанализировать JSON, вам нужно либо вернуть массив в JSON, что означало бы возврат DeviceDetails вместо объекта 1 выше, который отображается на ваш Observable<List<DeviceDetails>>, либо изменить вызов API get details для анализа объекта DeviceDetails Observable<DeviceDetails>

JSON, который вы опубликовали, будет означать изменение API на ожидаемое значение Observable<DeviceDetails>

После того, как вы это решите, вам нужно смоделировать объект Capabilities в вашем классе DeviceDetails, поскольку JSON не является массивом String. Это массив немоделированного объекта. (Строка, атрибут-оболочка, строка, строка)

...