简体   繁体   中英

GSON & Volley => Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $

This error drove me crazy I tried using different kind of Volley requests such as JsonArrayRequest and JsonObjectRequest but no luck !!

com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $


Here's the JSON response link => http://www.mocky.io/v2/58df83a80f00005922eaf4e1


Here's my model class

 public class Order {


@SerializedName("status")
@Expose
private String status; //status

@SerializedName("total")
@Expose
private float price; //total


@SerializedName("created_at")
@Expose
private Date time;

@SerializedName("line_items")
@Expose
public List<ItemsNum> lineItems = new ArrayList<ItemsNum>();

@SerializedName("sub_orders")
@Expose
public List<OrdersNum> subOrders = new ArrayList<OrdersNum>();

@SerializedName("shipping_address")
@Expose
public CusAddress cusAdress;

@SerializedName("customer")
@Expose
public Customer customer;


public Order(Customer customer, String status, float price, String cusPicURL, String firstName, String lastName, Date time, List<ItemsNum> lineItems, List<OrdersNum> subOrders, CusAddress cusAdress) {
    this.status = status;
    this.price = price;
    this.customer = customer;
    this.time = time;
    this.lineItems = lineItems;
    this.subOrders = subOrders;
    this.cusAdress = cusAdress;
}


public Date getTime() {
    return time;
}

public void setTime(Date time) {
    this.time = time;
}

public List<ItemsNum> getLineItems() {
    return lineItems;
}

public void setLineItems(List<ItemsNum> lineItems) {
    this.lineItems = lineItems;
}

public List<OrdersNum> getSubOrders() {
    return subOrders;
}

public void setSubOrders(List<OrdersNum> subOrders) {
    this.subOrders = subOrders;
}



public String getStatus() {
    return status;
}

public void setStatus(String status) {
    this.status = status;
}

public float getPrice() {
    return price;
}

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

public CusAddress getCusAdress() {
    return cusAdress;
}

public void setCusAdress(CusAddress cusAdress) {
    this.cusAdress = cusAdress;
}



// //////////// Inner-Classes


public class Customer {

    @SerializedName("avatar_url")
    @Expose
    private String cusPicURL; //avatar_url

    @SerializedName("first_name")
    @Expose
    private String firstName; //first_name

    @SerializedName("last_name")
    @Expose
    private String lastName; //last_name

    public String getCusPicURL() {
        return cusPicURL;
    }

    public void setCusPicURL(String cusPicURL) {
        this.cusPicURL = cusPicURL;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}

public class CusAddress {

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    @SerializedName("address_1")
    @Expose
    public String address;

    @SerializedName("city")
    @Expose
    public String city;

    @SerializedName("state")
    @Expose
    public String state;

    @SerializedName("country")
    @Expose
    public String country;
}

public class ItemsNum {

    @SerializedName("id")
    @Expose
    public Integer id;
}

public class OrdersNum {

    @SerializedName("id")
    @Expose
    public Integer id;
 }
}

My Adapter

  public class OrdersDataAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {


public Context context;
private int num;
ArrayList<Order> orders;


public OrdersDataAdapter(ArrayList<Order> orders, Context context) {
    this.orders = orders;

    this.context = context;
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.order_card, parent, false);

    return new NewOrderVH(v);

}


@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
    final Order order = this.orders.get(position);
    final NewOrderVH vh1 = (NewOrderVH) holder;
    vh1.setData(orders.get(position));

}

@Override
public int getItemCount() {
    return orders.size();
}


public class NewOrderVH extends RecyclerView.ViewHolder {


    private ImageView cusPic;
    private TextView cusName;
    private TextView CusAdress;
    private TextView vendorsNum;
    private TextView itemsNum;
    private TextView time;


    public NewOrderVH(View itemView) {
        super(itemView);


        orderCard = (CardView) itemView.findViewById(R.id.OrderCard);
        cusPic = (ImageView) itemView.findViewById(R.id.cusPic);
        cusName = (TextView) itemView.findViewById(R.id.cusName);
        CusAdress = (TextView) itemView.findViewById(R.id.CusAdress);
        vendorsNum = (TextView) itemView.findViewById(R.id.vendorsNum);
        itemsNum = (TextView) itemView.findViewById(R.id.itemsNum);
        time = (TextView) itemView.findViewById(R.id.time);


    }

    public void setData(final Order data) {


        cusName.setText(data.customer.getFirstName() + "" + data.customer.getLastName());
        Picasso.with(context).load(data.customer.getCusPicURL()).into(cusPic);
        time.setText(data.getTime().getMinutes());
        vendorsNum.setText(data.getSubOrders().size());
        itemsNum.setText(data.getLineItems().size());
        CusAdress.setText(data.cusAdress.getAddress() + "" + data.cusAdress.getCity() + "" + data.cusAdress.getCountry() + "" + data.cusAdress.getState());

    }


}

}

Request Method inside a Fragment

private void fetchPosts() {


    requestQueue = Volley.newRequestQueue(getActivity());

    StringRequest jRequest = new StringRequest(Request.Method.GET, ENDPOINT, onPostsLoaded, onPostsError);

    requestQueue.add(jRequest);

}


private final Response.Listener<String> onPostsLoaded = new Response.Listener<String>() {
    @Override
    public void onResponse(String response) {

        Gson gson = new Gson();
        Type listType = new TypeToken<List<Order>>() {}.getType();
        modelData = gson.fromJson(response, listType);


        adapter = new OrdersDataAdapter((ArrayList<Order>) modelData, getActivity());
        recyclerView.setAdapter(adapter);
        adapter.setMode(Attributes.Mode.Single);

    }
};

private final Response.ErrorListener onPostsError = new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        Log.e("PostActivity", error.toString());
    }
};

Here your problem is the orders object. You don't deserialize orders . You can solve this issue

public class OrderHolder{
    private List<Order> orders;

    public List<Order> getOrders(){ return orders; }

} 

OrderHolder holder = new Gson().fromJson(response, OrderHolder.class);
modelData = holder.getOrders();

or

JsonElement jelement = new JsonParser().parse(response);
JsonObject  jobject = jelement.getAsJsonObject();
String ordersString = jobject.get("orders").getAsString();

Gson gson = new Gson();
Type listType = new TypeToken<List<Order>>() {}.getType();
modelData = gson.fromJson(ordersString, listType);

Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $

This is easy to read: it means that you're parsing something like a list (so that BEGIN_ARRAY [ is expected), but the given JSON document starts with BEGIN_OBJECT { , that is caused by mismatched expected type. And it happens at the very beginning of the JSON document.

Apart from the mismanc 's answer , there is also a third-way, that requires a slightly more code (can be extracted to a method though), but it does not require both wrappers or intermediate JSON trees like JsonElement and its subclasses (that may consume slightly more memory for the 1st case, and definitely more memory for the second case because the whole intermediate JSON tree must be stored in memory).

// JsonReader allows to read JSON token stream
try ( final JsonReader jsonReader = new JsonReader(new StringReader(JSON)) ) {
    // Just skip the leading `{`
    jsonReader.beginObject();
    // Check if the next property name is what we're expecting
    final String name = jsonReader.nextName();
    if ( !name.equals("orders") ) {
        throw new MalformedJsonException("Unexpected: " + name);
    }
    // If it's fine, then we:
    // * are assuming the JsonReader "pointer" is at the property value now
    // * are asking Gson for the proper type adapter
    @SuppressWarnings("unchecked")
    final TypeAdapter<List<Order>> typeAdapter = (TypeAdapter<List<Order>>) gson.getAdapter(TypeToken.get(orderListType));
    // Just read the orders
    final List<Order> orders = typeAdapter.read(jsonReader);
    System.out.println(orders);
}

JSON schema to POJO automatic converters/generators do not always create optimal mappings. For example:

  • Both ItemsNum and OrdersNum are represented by the same POJO structure, however it can be final class Id { @SerializedName("id") final Integer id = null; } final class Id { @SerializedName("id") final Integer id = null; } .
  • Customer avatar_url is represented as java.lang.String , but it can be java.net.URL too.

Minor notes:

  • Gson class instances not required to be created every time you need deserialization. They are fully thread-safe and can be instantiated once and then re-used easily. Also, created and configured else Gson instance can be just injected to your code.
  • Type instances produced with TypeToken s can be considered immutable value objects therefore to be cached into a static final field too.

here's the correct answer, I was using wrong type of request..!

  JsonObjectRequest jsObjRequest = new     
  JsonObjectRequest(Request.Method.GET,ENDPOINT,null,
            new Response.Listener<JSONObject>() {
                @Override
                public void onResponse(JSONObject response) {

                    Gson gson = new Gson();
                    Type listType = new TypeToken<List<Order>>() {}.getType();
                    try {

                        List<Order> o = new ArrayList<>();
                        o = gson.fromJson(response.getJSONArray("orders").toString(), listType);
                        if(o != null && !o.isEmpty()){

                            modelData.addAll(o);
                            adapter.notifyDataSetChanged();
                        }

                    } catch (JSONException e) {
                        e.printStackTrace();
                    }

                }
            },
            new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {

                }
            });

    requestQueue.add(jsObjRequest);

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM