如何使用Retrofit2

时间:2019-12-08 05:07:11

标签: android json rest retrofit2

最近在制作一个使用Rest API的项目时,我决定使用Retrofit。但是,在向上述API发送GET请求后,我遇到了一个问题,尽管有人提出了类似的问题,但我仍然找不到解决方案。

我遇到的问题(无论如何我都认为这是问题)是API提供的响应会根据给定的输入而变化,因此我的POJO并不总是匹配。更具体地说,有时我得到单个对象而不是列表作为字段。该行的错误要点在以下行中进行了很好的描述:

Caused by: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 250 column 20 path $.LocationList.CoordLocation

从API接收到的Json在大多数情况下将看起来像这样:

{
"LocationList": {
  "StopLocation": [
    {
      "name": String,
      "x": String,
      "y": String,
      "id": String
    },
...
   ],
   "CoordLocation": [
     {
      "name": String,
      "x": String,
      "y": String,
      "type": String
     },
...
    ]
  }
 }
}

这很好,因为我的LocationList POJO定义为同时包含CoordLocations和StopLocations的列表。但是,当某些输入的API仅使用CoordLocation的单个StopLocation进行响应时,由于接收到的Json是单个对象而不是具有单个对象条目的数组,所有这些都将崩溃:

    {
"LocationList": {
  "StopLocation": [
    {
      "name": String,
      "x": String,
      "y": String,
      "id": String
    },
...
   ],
  "CoordLocation": {
      "name": String,
      "x": String,
      "y": String,
      "type": String
  }
 }
}

所以我的问题是如何解决这个问题?到目前为止,我已经看到有人建议使用自定义TypeAdapter和Deserializers,但是每次我尝试遵循一种解决方案时,都会遇到麻烦,因为情况略有不同,并且因为我对所有与API相关的事物都是陌生的。

2 个答案:

答案 0 :(得分:0)

在这种情况下,我建议您使用JsonElement作为答复。当您获得响应检查时,检查它是JsonObject还是JsonArray,然后将响应对象转换为pojo或其他内容。

答案 1 :(得分:0)

所以事实证明,自定义TypeAdapter是必经之路,而我只是没有足够的经验来编写自己的前几次尝试。但是我终于成功了,结果看起来像这样:

package com.example.android_navigation.RejseplanenAPI;

import com.example.android_navigation.RejseplanenAPI.POJOs.CoordLocation;
import com.example.android_navigation.RejseplanenAPI.POJOs.Location;
import com.example.android_navigation.RejseplanenAPI.POJOs.StopLocation;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class LocationTypeAdapter extends TypeAdapter<Location> {

@Override
public Location read(JsonReader reader) throws IOException {
    Location location = new Location();
    Gson gson = new Gson();

    JsonParser parser = new JsonParser();
    JsonObject jsonObject = (JsonObject)parser.parse(reader);
    JsonObject locationListObject = jsonObject.get("LocationList").getAsJsonObject();
    JsonElement stopLocations = locationListObject.get("StopLocation");
    JsonElement coordLocations = locationListObject.get("CoordLocation");

    if(stopLocations != null) {
        if(stopLocations.isJsonArray()){
            StopLocation[] stopArray = gson.fromJson(stopLocations, StopLocation[].class);
            location.getLocationList().setStopLocations(Arrays.asList(stopArray));
        }
        else if (stopLocations.isJsonObject()) {
            StopLocation stopLocation = gson.fromJson(stopLocations, StopLocation.class);
            List<StopLocation> stopLocationsList = new ArrayList<>();
            stopLocationsList.add(stopLocation);
            location.getLocationList().setStopLocations(stopLocationsList);
        }
    }
    if(coordLocations != null) {
        if (coordLocations.isJsonArray()) {
            CoordLocation[] coordArray = gson.fromJson(coordLocations, CoordLocation[].class);
            location.getLocationList().setCoordLocations(Arrays.asList(coordArray));
        } else if (coordLocations.isJsonObject()) {
            CoordLocation coordLocation = gson.fromJson(coordLocations, CoordLocation.class);
            List<CoordLocation> coordLocationsList = new ArrayList<>();
            coordLocationsList.add(coordLocation);
            location.getLocationList().setCoordLocations(coordLocationsList);
        }
    }
    return location;
}

@Override
public void write(JsonWriter out, Location value) throws IOException {

}
}

然后在构建Retrofit实例中使​​用的Gson时将其添加:

    private Gson gson = new GsonBuilder()
        .registerTypeAdapter(Location.class, new LocationTypeAdapter())
        .setLenient()
        .create();
    private Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://xmlopen.rejseplanen.dk/bin/rest.exe/")
        .addConverterFactory(GsonConverterFactory.create(gson))
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .build();