0

Trying some different methods to parse nested Json that is less than user friendly. With the logger I can see the result coming in correctly but the log shows error

  com.squareup.moshi.JsonDataException: Expected a string but was BEGIN_OBJECT at path $.capabilities[1]

I cannot for the life of me figure out how to parse the Attribute array. I have tried doing <List<Attribute>> and Attribute and it does not change the result. Is there a way to convert the Attribute array into a list? Very new at coding in Android so looking for some help.

JSON to parse

{
    "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

data class DeviceDetails(
    @Json(name="CapabilitiesList")
    var attributeList: Attribute,
    @Json(name="CapabilitiesList")
    val capabilities: List<String>,
    @Json(name="CommandsList")
    val commands: List<String>,
    var id: String = "",
    var label: String = "",
    var name: String = ""

)

data class Attribute(
    val currentValue: String,
    val dataType: String,
    val name: String,
    @Json(name="AttributesValues")
    val values: List<String>
)

DeviceDetailsAPI

interface DeviceDetailsAPI {
        @GET("devices/65")
        fun getDeviceDetails(@Query("access_token") access_token: String):
            Deferred<DeviceDetails>

    companion object{
        operator fun invoke(): DeviceDetailsAPI {
            //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 okHttpClient = OkHttpClient.Builder()
                .build()

            return Retrofit.Builder()
                .client(okHttpClient)
                .baseUrl("http://xxx.xxx.xxx.xxx/apps/api/109/")
                .addCallAdapterFactory(CoroutineCallAdapterFactory())
                .addConverterFactory(MoshiConverterFactory.create())
                .client(client)
                .build()
                .create(DeviceDetailsAPI::class.java)
        }
    }
}

MainActivity

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val API_KEY = "xxxxxxxx"
        val testapiService = DeviceListAPI()
        val testapiDetails = DeviceDetailsAPI()

        //GlobalScope.launch (Dispatchers.Main) {
            //val DeviceListAPI = testapiService.getAllDevices(access_token = API_KEY).await()
            //textViewID.text = DeviceListAPI.toString()
        //}

        GlobalScope.launch (Dispatchers.Main) {
            val DeviceDetailsAPI = testapiDetails.getDeviceDetails(access_token = API_KEY).await()
            textViewID.text = DeviceDetailsAPI.toString()
        }

    }

}

1 Answer 1

1

The apparent problem is that the "capabilities": ... in the JSON block is a mixed type list, but you declare it as val capabilities: List<String>. Hence it fails when it hits the

  {
    "attributes": [
      {
        "name": "switch",
        "dataType": null
      }
    ]
  },

item. It's hard to guess how this item relates to the capabilities, but as it currently stands it looks like this will require a pretty complicated custom Moshi adapter to be able to parse this into a meaningful data structure.

Sign up to request clarification or add additional context in comments.

6 Comments

it actually fails when it reaches this part "capabilities": ["Switch", { as it's expecting a list of String and it finds an object instead
You are right! So my capabilities block is listed as a List<String> but its getting an object. Any suggestions how to do this and make it work?
Can you get the API developer to return a more uniformly structured object, or at least document the return object schema? From just this example it's hard to tell what the general schema of the object actually is, so it's impossible to know how construct a useful data structure from it.
I have asked but I doubt it goes anywhere. If they do not change, what would my adapter have to do? I've looked at the "case" statements but those are way over my head at this point.
At least the API should have a documented schema somewhere. If it doesn't you're just playing a guessing game. Far less ideal, but a different possible approach is to determine what you minimally need from the API and write a custom Moshi adapter to ignore everything else. Granted this isn't easy, and you might need more help than what you can get on SO.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.