0

I'm working on an inventory app in Kotlin for work and I became stumped when trying to populate a list view in one of my activities. For some reason, it's throwing the following error:

 W/System.err: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $

With the OkHttpClient, I can see my Json result:

{
"result":
[
    {
        "stockOutID":"22",
        "skuUsageForStockOutID":"37",
        "masterStockListID":"4",
        "partNumber":"51220",
        "altPartNumber":"Z-234",
        "quantity":"2.0",
        "price":"6.00","total":"12.00",
        "skuType":"INVENTORY-ITEM"
    }
],
"targetUrl":null,
"success":true,
"error":null,
"unAuthorizedRequest":false,
"__abp":true
}

From that, I used JsonScheme2Pojo and got the following result which I placed in a file named "StockOutItemsResponse.java":

public class Result {

    @SerializedName("stockOutID")
    @Expose
    private String stockOutID;
    @SerializedName("skuUsageForStockOutID")
    @Expose
    private String skuUsageForStockOutID;
    @SerializedName("masterStockListID")
    @Expose
    private String masterStockListID;
    @SerializedName("partNumber")
    @Expose
    private String partNumber;
    @SerializedName("altPartNumber")
    @Expose
    private String altPartNumber;
    @SerializedName("quantity")
    @Expose
    private String quantity;
    @SerializedName("price")
    @Expose
    private String price;
    @SerializedName("total")
    @Expose
    private String total;
    @SerializedName("skuType")
    @Expose
    private String skuType;

    public String getStockOutID() {
        return stockOutID;
    }

    public void setStockOutID(String stockOutID) {
        this.stockOutID = stockOutID;
    }

    public String getSkuUsageForStockOutID() {
        return skuUsageForStockOutID;
    }

    public void setSkuUsageForStockOutID(String skuUsageForStockOutID) {
        this.skuUsageForStockOutID = skuUsageForStockOutID;
    }
    
    public String getMasterStockListID() {
        return masterStockListID;
    }

    public void setMasterStockListID(String masterStockListID) {
        this.masterStockListID = masterStockListID;
    }

    public String getPartNumber() {
        return partNumber;
    }

    public void setPartNumber(String partNumber) {
        this.partNumber = partNumber;
    }

    public String getAltPartNumber() {
        return altPartNumber;
    }

    public void setAltPartNumber(String altPartNumber) {
        this.altPartNumber = altPartNumber;
    }

    public String getQuantity() {
        return quantity;
    }

    public void setQuantity(String quantity) {
        this.quantity = quantity;
    }

    public String getPrice() {
        return price;
    }

    public void setPrice(String price) {
        this.price = price;
    }

    public String getTotal() {
        return total;
    }

    public void setTotal(String total) {
        this.total = total;
    }

    public String getSkuType() {
        return skuType;
    }

    public void setSkuType(String skuType) {
        this.skuType = skuType;
    }
}

Because the response has a "result" as the root, I added the following to a file named "StockOutItemResult.java" that also came from JsonSchema2Pojo:

@SerializedName("result")
@Expose
private List<StockOutItemsResponse> result = null;
@SerializedName("targetUrl")
@Expose
private Object targetUrl;
@SerializedName("success")
@Expose
private Boolean success;
@SerializedName("error")
@Expose
private Object error;
@SerializedName("unAuthorizedRequest")
@Expose
private Boolean unAuthorizedRequest;
@SerializedName("__abp")
@Expose
private Boolean abp;

public List<StockOutItemsResponse> getResult() {
    return result;
}

public void setResult(List<StockOutItemsResponse> result) {
    this.result = result;
}

public Object getTargetUrl() {
    return targetUrl;
}

public void setTargetUrl(Object targetUrl) {
    this.targetUrl = targetUrl;
}

public Boolean getSuccess() {
    return success;
}

public void setSuccess(Boolean success) {
    this.success = success;
}

public Object getError() {
    return error;
}

public void setError(Object error) {
    this.error = error;
}

public Boolean getUnAuthorizedRequest() {
    return unAuthorizedRequest;
}

public void setUnAuthorizedRequest(Boolean unAuthorizedRequest) {
    this.unAuthorizedRequest = unAuthorizedRequest;
}

public Boolean getAbp() {
    return abp;
}

public void setAbp(Boolean abp) {
    this.abp = abp;
}

My request is named, "StockOutItemsRequest" and it looks like this:

public class StockOutItemsRequest {
public String companyID;
public String warehouseID;

public StockOutItemsRequest(String companyID, String warehouseID) {
    this.companyID = companyID;
    this.warehouseID = warehouseID;
  }
}

Then comes my adapter for the list view, named "StockOutItemsAdapter.java":

public class StockOutItemsAdapter extends ArrayAdapter<StockOutItemsResult> {

public StockOutItemsAdapter(Context context, int resource, ArrayList<StockOutItemsResult> objects) {
    super(context, resource, objects);
}

@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {

    StockOutItemsResult stockOutItemsResult = getItem(position);

    if(convertView == null) {
        convertView = LayoutInflater.from(getContext()).inflate(R.layout.stockout_adapter_view_layout, parent, false);
    }

    // Lookup view for data population
    TextView tv_sku_id = (TextView) convertView.findViewById(R.id.txt_sku_id);
    TextView tv_sku = (TextView) convertView.findViewById(R.id.txt_sku);
    TextView tv_alt_sku = (TextView) convertView.findViewById(R.id.txt_alt_sku);
    TextView tv_quantity = (TextView) convertView.findViewById(R.id.txt_quantity);
    TextView tv_price = (TextView) convertView.findViewById(R.id.txt_price);
    TextView tv_total = (TextView) convertView.findViewById(R.id.txt_total);

    tv_sku_id.setText(stockOutItemsResult.skuUsageForStockOutID);
    tv_sku.setText(stockOutItemsResult.partNumber);
    tv_alt_sku.setText(stockOutItemsResult.altPartNumber);
    tv_quantity.setText(stockOutItemsResult.quantity);
    tv_price.setText(stockOutItemsResult.price);
    tv_total.setText(stockOutItemsResult.total);

    return convertView;
 }
}

And my interface:

@GET("getStockOutItems")
Call<ArrayList<StockOutItemsResult>> getStockOutItems(
        @Query("companyID") String companyID,
        @Query("warehouseID") String warehouseID);

I also pass some headers with that.

I then placed the following in my StockOutActivity.kt:

// global arrayList
private var skuItems: ArrayList<StockOutItemsResult>? = null

using Retrofit2, I call the API:

    call.enqueue(object : Callback<ArrayList<StockOutItemsResult>> {
        @RequiresApi(Build.VERSION_CODES.KITKAT)
        @SuppressLint("SetTextI18n")
        override fun onResponse(
            call: Call<ArrayList<StockOutItemsResult>>,
            response: Response<ArrayList<StockOutItemsResult>>
        ) {
            if (response.isSuccessful) {
                val responseString: ArrayList<StockOutItemsResult>? = response.body()

                // store values to access later
                skuItems = responseString

                if (responseString != null) {
                    val skuList = ArrayList(responseString)

                    var adapter = StockOutItemsAdapter(
                        this@StockOutActivity,
                        R.layout.stockout_adapter_view_layout,
                        skuList
                    )

                    mListViewLayout.adapter = adapter

                    swipeMenu()
                }

            } else {

            }
        }

        override fun onFailure(
            call: Call<ArrayList<StockOutItemsResult>>,
            t: Throwable
        ) {
            t.printStackTrace()
            Toast.makeText(this@StockOutActivity, "error :(", Toast.LENGTH_SHORT).show()
        }
    })

When I run the app in the simulator, all of my other calls work fine, but when I get to this part, that is what throws the error.

My resultset shows one record, but it will, on most occasions have multiple ones so that's why I need to convert it to a list.

Can someone show me the problem and what I need to do to fix it because I have about 4 more activities to develop and I will learn from this one.

Thanks

Edit: Below is the log which shows what's happening after receiving the json results:

I/okhttp.OkHttpClient: {"result":[{"stockOutID":"22","skuUsageForStockOutID":"37","masterStockListID":"4","partNumber":"51220","altPartNumber":"Z-234","quantity":"2.0","price":"6.00","total":"12.00","skuType":"INVENTORY-ITEM"},{"stockOutID":"22","skuUsageForStockOutID":"38","masterStockListID":"81","partNumber":"3950TKS","altPartNumber":"","quantity":"1.0","price":"275.00","total":"275.00","skuType":"NON-INVENTORY-ITEM"}],"targetUrl":null,"success":true,"error":null,"unAuthorizedRequest":false,"__abp":true} <-- END HTTP (489-byte body) W/System.err: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $ W/System.err: at com.google.gson.stream.JsonReader.beginArray(JsonReader.java:350) at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:80) W/System.err: at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61) at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:40) at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:27) at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:243) at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:153) at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519) W/System.err: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) at java.lang.Thread.run(Thread.java:923)

I'm calling my service this way:

@GET("api/mobistock/getStockOutItems")
Call<StockOutItemsResult> getStockOutItems(
        @Query("companyID") String companyID,
        @Query("warehouseID") String warehouseID,
        @Header("Authorization") String token,
        @Header("Content-Type") String cType,
        @Header("Abp.TenantId") String tenantId);

and in my activity ...

        call.enqueue(object : Callback<StockOutItemsResult> {
        override fun onResponse(
            call: Call<StockOutItemsResult>, response: Response<StockOutItemsResult>
        ) {
            if(response.isSuccessful) {

               **//stockOutItems_Array = response.body() asArrayList<StockOutItemsResult> ** //<--- was trying to see what was being returned, but it errors out.
            }
            else {
                //Log.e("Response Error", "Something went wrong")
            }
        }

        override fun onFailure(call: Call<StockOutItemsResult>, t: Throwable) {
            Log.e("Response Failure Error", "Something went wrong")
        }

    })
4
  • Can you please update Json result properly bcoz currently displayed Json result is not well formed. There are two closing Array is there but only one Opening Array is visible. Commented Jan 8, 2021 at 12:51
  • @SerializedName("result") @Expose private List<Result > result = null; update this Commented Jan 8, 2021 at 12:55
  • Why do you define getStockOutItems to return an ArrayList<StockOutItemsResult>? The data looks more like it returns an Result. Commented Jan 8, 2021 at 14:06
  • @Robert, When I switched code to use Abp Boilerplate from another custom API that I built, it didn't have the "Result": root in the Json. I actually had this working, until I made that switch, so now because the "Result" is there, I had to implement code to handle that. With other APIs that I created, adding code to handle "Result": works fine ... it's just that dreaded ArrayList that I can't seem to figure out Commented Jan 8, 2021 at 15:22

1 Answer 1

1

Your JSON response from the server is not valid. Try this online JSON validator with your response from the server and verify that it's valid.

EDIT

From your request:

@GET("getStockOutItems")
Call<ArrayList<StockOutItemsResult>> getStockOutItems(
    @Query("companyID") String companyID,
    @Query("warehouseID") String warehouseID);

You expect an array to be returned, but the server sends an object. So if StockOutItemsResult is the object that represents your response you should write your method like here:

@GET("getStockOutItems")
Call<StockOutItemsResult> getStockOutItems(
    @Query("companyID") String companyID,
    @Query("warehouseID") String warehouseID);
Sign up to request clarification or add additional context in comments.

7 Comments

It's valid as I verified it before coming across this issue as well as with other online validators. I just typed it wrong in the post ... I'll edit it
@Janou I edited the answer. It seems like you are expecting the list as your response, but the actual response is an object with the list inside.
wrapped in the "result" is the data that I need ... what's between the brackets [ ] ... In my StockOutItemsResponse, that is laid out, however, because the "result" was rooted in the front, I made "StockOutItemsResult" to handle that which then plugs the "StockOutItemsResponse" inside of it. I did this with non-arraylist results and it solved the problem. However, it seemed to have become tricky with lists.
because I never did it with an ArrayList and can't get it to work while with non-arraylist calls, it works fine with the "result" root in the json response
If my answer helped you, please do accept it.
|

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.