0

I have an API call that is looking for an array and objects basically in the same spot. I am trying to call the API and getting the following error. io.reactivex.exceptions.OnErrorNotImplementedException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $

How can I call both the root items and the sub items in the attributes class?

I have some logging in the app and the URL is correct, but the response is the problem

This is the API -

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

Using json mapping tool this is what it defines for the layout

public class Attribute {

@SerializedName("name")
@Expose
private String name;
@SerializedName("currentValue")
@Expose
private String currentValue;
@SerializedName("dataType")
@Expose
private String dataType;
@SerializedName("values")

seperate class it defined

public class Example {

@SerializedName("id")
@Expose
private String id;
@SerializedName("name")
@Expose
private String name;
@SerializedName("label")
@Expose
private String label;
@SerializedName("attributes")
@Expose
private List<Attribute> attributes = null;
@SerializedName("capabilities")
@Expose
private List<String> capabilities = null;
@SerializedName("commands")
@Expose
private List

Device Details Class

data class DeviceDetails(
    val attributes: List<Attribute>,
    val capabilities: List<String>,
    val commands: List<String>,
    val id: String,
    val label: String,
    val name: String
)

Attribute Class

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

Interface Adapter

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

Adapater

class PostItemDetailsAdapter(private val postdetailsList: List<Attribute>, private val context: Context) :
    RecyclerView.Adapter<PostItemDetailsAdapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(
            LayoutInflater.from(context).inflate(R.layout.post_item_details,
                parent, false))
    }

    override fun getItemCount(): Int {
        return postdetailsList.size
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.itemView.txtPostName.text = this.postdetailsList[position].name
        holder.itemView.txtPostCurrent.text = this.postdetailsList[position].currentValue
        holder.itemView.txtPostDataType.text = this.postdetailsList[position].dataType

    }
    class ViewHolder(view: View) : RecyclerView.ViewHolder(view)
}

Activity2

class Activity2 : AppCompatActivity() {

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

        //Debugging URL//
        val interceptor : HttpLoggingInterceptor = HttpLoggingInterceptor().apply {
            this.level = HttpLoggingInterceptor.Level.BODY
        }

        val client : OkHttpClient = OkHttpClient.Builder().apply {
            this.addInterceptor(interceptor)
        }.build()
        //Debugging URL//

        Log.d("TAG", "Activity 2")

        val retrofit = Retrofit.Builder().addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .baseUrl("http://xxx.xxx.xxx.xxx/apps/api/109/")
            .client(client)
            .build()
        //Base of API Call//


        //Build List of Devices//
        val postsApi = retrofit.create(INetworkAPIDetails::class.java)
        var response = postsApi.getAllDetails(access_token = "xxxx")

        //Displaying List//
        response.observeOn(AndroidSchedulers.mainThread()).subscribeOn(IoScheduler()).subscribe {
            rv__list_posts_details.adapter = PostItemDetailsAdapter(it, this)
        }
    }
}

1 Answers1

1

Form the JSON you've shared, your INetworkAPIDetails should be:

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

The structure seems to be DeviceDetails, plus the List meant it was expecting an array of objects, before the correction an array of objects of type Attribute.

Also, you have a tricky case in the capabilities array: in your data structure is defined as a List<String>... but the second element "Switch" and "Configuration" is not a String, is a complete Object.

For this case see How parse json array with multiple objects by gson? or Using GSON to parse array with multiple types

Xavier Rubio Jansana
  • 6,388
  • 1
  • 27
  • 50
  • I did that but now I get the error "expected string but was given object". – user1722885 Apr 17 '19 at 19:24
  • Ok, now I've realized you have a tricky case in the `capabilities` array: in your data structure is defined as a `List`... but the second element `"Switch"` and `"Configuration"` is not a `String`, is a complete `Object`. I'm pretty sure that's the error now. For this case see https://stackoverflow.com/questions/32451666/how-parse-json-array-with-multiple-objects-by-gson or https://stackoverflow.com/questions/5377827/using-gson-to-parse-array-with-multiple-types Adding this to the Answer. – Xavier Rubio Jansana Apr 17 '19 at 21:50
  • is there a way to grab certain items from inside the array rather than grabbing all the items? From what I am reading this is very java unfriendly and will take some trickery to get working. – user1722885 Apr 17 '19 at 22:39
  • All the possible options imply "getting your hands dirty". Probably the ones I linked are the cleanest. – Xavier Rubio Jansana Apr 18 '19 at 11:48