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)
        }
    }
}
2
  • Are you sure the API will return array ? Commented Apr 17, 2019 at 18:06
  • if you go here jsonschema2pojo.org and plug in that, it shows string and array as values Commented Apr 17, 2019 at 18:13

1 Answer 1

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

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

4 Comments

I did that but now I get the error "expected string but was given object".
Ok, now I've realized 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. I'm pretty sure that's the error now. For this case see stackoverflow.com/questions/32451666/… or stackoverflow.com/questions/5377827/… Adding this to the Answer.
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.
All the possible options imply "getting your hands dirty". Probably the ones I linked are the cleanest.

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.