当回复有时是一个对象而有时是一个数组时,如何在使用改进时解析JSON回复?

时间:2016-02-02 06:34:57

标签: java json retrofit rx-java

我正在使用Retrofit来获取JSON回复。

以下是我的实施部分 -

@GET("/api/report/list")
Observable<Bills> listBill(@Query("employee_id") String employeeID);

和班级比尔是 -

public static class Bills {
    @SerializedName("report")
    public ArrayList<BillItem> billItems;
}

BillItem类如下 -

public static class BillItem {
    @SerializedName("id")
    Integer entryID;
    @SerializedName("employee_id")
    Integer employeeDBID;
    @SerializedName("type")
    String type;
    @SerializedName("subtype")
    String subtype;
    @SerializedName("date")
    String date;
    @SerializedName("to")
    String to;
    @SerializedName("from")
    String from;
    @SerializedName("amount")
    Double amount;
    @SerializedName("location")
    String location;
    @SerializedName("remark")
    String remark;
    @SerializedName("ispaid")
    String isPaid;
    @SerializedName("created_at")
    String createdAt;
    @SerializedName("updated_at")
    String updateAt;
}

问题是REST API有时返回BillItem个对象的数组,但有时它只是key-value对。如何处理这种情况?

收到此回复后,一切正常,因为JSONArray已映射到ArrayList<BillItem> -

{
   "emp":{
      "id":41,
      "name":"",
      "email":"",
      "created_at":"2016-02-01 10:36:38",
      "updated_at":"2016-02-01 10:36:38"
   },
   "report":[
      {
     "id":175,
     "employee_id":41,
     "type":"Travel",
     "subtype":"Car",
     "date":"2016-02-02 00:00:00",
     "to":"gaha",
     "from":"hshsj",
     "amount":"64",
     "location":"",
     "remark":"shhs",
     "ispaid":false,
     "created_at":"2016-02-01 13:52:52",
     "updated_at":"2016-02-01 13:52:52"
      },
      {
     "id":179,
     "employee_id":41,
     "type":"Travel",
     "subtype":"Car",
     "date":"2016-02-01 00:00:00",
     "to":"Gsh",
     "from":"Dgdh",
     "amount":"7646",
     "location":"",
     "remark":"Shsh",
     "ispaid":false,
     "created_at":"2016-02-01 14:39:48",
     "updated_at":"2016-02-01 14:39:48"
      }
   ]
}

但是,有时响应是这样的,它会给出JsonSyntaxException -

{
   "emp":{
      "id":41,
      "name":"",
      "email":"",
      "created_at":"2016-02-01 10:36:38",
      "updated_at":"2016-02-01 10:36:38"
   },
   "report":{
      "1":{
     "id":175,
     "employee_id":41,
     "type":"Travel",
     "subtype":"Car",
     "date":"2016-02-02 00:00:00",
     "to":"gaha",
     "from":"hshsj",
     "amount":"64",
     "location":"",
     "remark":"shhs",
     "ispaid":false,
     "created_at":"2016-02-01 13:52:52",
     "updated_at":"2016-02-01 13:52:52"
      },
      "2":{
     "id":179,
     "employee_id":41,
     "type":"Travel",
     "subtype":"Car",":"2016-02-01 00:00:00",
     "to":"Gsh",
     "from":"Dgdh",
     "amount":"7646",
     "location":"",
     "remark":"Shsh",
     "ispaid":false,
     "created_at":"2016-02-01 14:39:48",
     "updated_at":"2016-02-01 14:39:48"
      },
      "0":{
     "id":181,
     "employee_id":41,
     "type":"Travel",
     "subtype":"Car",
     "date":"2016-02-01 00:00:00",
     "to":"ggg",
     "from":"vg",
     "amount":"0",
     "location":"",
     "remark":"cvv",
     "ispaid":false,
     "created_at":"2016-02-01 17:43:43",
     "updated_at":"2016-02-01 17:43:43"
      },
      "3":{
     "id":182,
     "employee_id":41,
     "type":"Travel",
     "subtype":"Car",
     "date":"2016-02-01 00:00:00",
     "to":"Haha",
     "from":"Ahah",
     "amount":"0",
     "location":"",
     "remark":"Ahah",
     "ispaid":false,
     "created_at":"2016-02-01 17:53:58",
     "updated_at":"2016-02-01 17:53:58"
      }
   }
}

如何处理这样的回复?

2 个答案:

答案 0 :(得分:6)

使用Gson反序列化器时,可以检查JsonElement中的类型:

<title><?php if (is_front_page()) { bloginfo('name');echo ' | ';bloginfo('description'); } elseif ( is_category() ) { single_cat_title(); echo ' - ' ; bloginfo('name'); }
elseif (is_single() ) { single_post_title(); }
elseif (is_page() ) { bloginfo('name'); echo ' | '; single_post_title(); }
else { wp_title('',true); } ?></title>

参见示例:Gson deserialization - Trying to parse a JSON to an Object

答案 1 :(得分:1)

正如answer @Gavriel所述,需要编写自定义Deserializer

这是我的实现(它可能包含多余的代码) -

实际API定义 -

public interface ServerAPI {
    public static final String ENDPOINT = "http://xyz";

    @GET("/api/report/list")
    Observable<Bills> listBill(@Query("employee_id") String employeeID);

    public static class BillItem {
        @SerializedName("id")
        public Integer entryID;
        @SerializedName("employee_id")
        public Integer employeeDBID;
        @SerializedName("type")
        public String type;
        @SerializedName("subtype")
        public String subtype;
        @SerializedName("date")
        public String date;
        @SerializedName("to")
        public String to;
        @SerializedName("from")
        public String from;
        @SerializedName("amount")
        public Double amount;
        @SerializedName("location")
        public String location;
        @SerializedName("remark")
        public String remark;
        @SerializedName("ispaid")
        public String isPaid;
        @SerializedName("created_at")
        public String createdAt;
        @SerializedName("updated_at")
        public String updateAt;
    }

    public static class Bills {
        @SerializedName("report") // This seems to serve no purpose.
        public ArrayList<BillItem> billItems = new ArrayList<>(); // It was needed to initialize the ArrayList.

        public void add(BillItem billItem) {
            billItems.add(billItem);
        }
    }

}

这是我们在收到回复时实际创建BillItem类的地方。

public class App extends Application {
    private static App instance;
    private static ServerAPI serverAPI;

    public static ServerAPI getServerAPI() {
        return serverAPI;
    }

    @Override
    public void onCreate() {
        super.onCreate();

        Gson gson = new GsonBuilder()
                .registerTypeAdapter(BillItem.class, new BillItemDeserializer())
                .registerTypeAdapter(Bills.class, new BillsDeserializer())
                .create();

        instance = this;
        serverAPI = new RestAdapter.Builder()
                .setEndpoint(ServerAPI.ENDPOINT)
                .setConverter(new GsonConverter(gson))
                .setLogLevel(RestAdapter.LogLevel.FULL)
                .setLog(new RestAdapter.Log() {
                    @Override
                    public void log(String message) {
                        Log.v("Retrofit", message);
                    }
                })
                .build().create(ServerAPI.class);
    }

    // This has nothing inside of it, it still works.
    public class BillItemDeserializer implements JsonDeserializer<BillItem> {

        @Override
        public BillItem deserialize(JsonElement json, Type typeOfT,
                                    JsonDeserializationContext context) throws
                JsonParseException {
            return null;
        }
    }

    private class BillsDeserializer implements JsonDeserializer<Bills> {
        @Override
        public Bills deserialize(JsonElement json, Type typeOfT,
                                 JsonDeserializationContext context) throws
                JsonParseException {
            Bills value = new Bills();
            Gson gson = new Gson();

            json = json.getAsJsonObject().get("report");
            if (json.isJsonArray()) {
                for (JsonElement element : json.getAsJsonArray()) {
                    value.add(gson.fromJson(element, BillItem.class));
                }
            } else {
                JsonElement element = json.getAsJsonObject();
                Set<Map.Entry<String, JsonElement>> entries = element.getAsJsonObject().entrySet();
                for (Map.Entry<String, JsonElement> entry : entries) {
                    value.add(gson.fromJson(entry.getValue(), BillItem.class));
                }
            }
            return value;
        }
    }
}

这是我们收到数据的Subscription对象。

Subscription subscription = App.getServerAPI()
        .listBill(String.valueOf(employeeID))
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Observer<ServerAPI.Bills>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {
                Log.i(TAG, "ERROR: Value returned: " + e.getMessage());

                e.printStackTrace();
            }

            @Override
            public void onNext(ServerAPI.Bills bills) {
                for (ServerAPI.BillItem item : bills.billItems) {
                    Log.i(TAG, "onNextBillItem: " + item);
                }
            }

        });