与Kotlin短暂?

时间:2019-05-03 20:46:10

标签: android kotlin retrofit retrofit2 moshi

试图显示设计不佳的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类

    @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>
)

Details 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调用,它显示“属性”数组仍在解析中。我究竟做错了什么?为什么瞬态不起作用?任何帮助表示赞赏。

2 个答案:

答案 0 :(得分:2)

该错误表明该问题存在于作为根源的path $上。

您的问题是您说的是JSON是List<DeviceDetails>,但实际上它实际上只是一个DeviceDetails

答案 1 :(得分:0)

正如@Kiskae回答的那样,该类需要一个DeviceDetails列表,而当前的JSON将是一个DeviceDetails对象。

通常,模型类中的列表与[ {object 1}, {object 2} ]的JSON中的数组语法相对应

您的根JSON是一个对象-要对其进行更改,以便您可以解析JSON,您需要在JSON中返回一个数组,这将意味着在对象1上方返回DeviceDetails,该对象上方映射到您的Observable<List<DeviceDetails>>或更改获取详细信息api调用,以便它解析DeviceDetails对象Observable<DeviceDetails>

您发布的JSON意味着更改API以期望Observable<DeviceDetails>

解决此问题后,您将需要在DeviceDetails类中为Capabilities对象建模,因为JSON不是String数组。它是未建模对象的数组。 (字符串,属性包装程序,字符串,字符串)