首页 文章

在Retrofit GET请求之后如何使用GSON解析此JSON响应?

提问于
浏览
1

我正在尝试在Retrofit GET请求之后使用GSON解析JSON响应 . 我不需要所有的键和值,所以我只是@Expose我需要的那些并指示解析器这样做 . 请求激活并且响应变得干净,但是查看logcat我发现这个错误,这显然表明POJO模型格式化或实现不好:

04-09 12:16:01.679 5604-5604/? V/Retrofit error﹕ retrofit.converter.ConversionException: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $

这是JSON响应(在GET请求后返回ok):

{
   objects: [
   {
     rating: 0.97,
     name: "High Line",
     ranking: 0,
     url: "http://www.thehighline.org",
     price: null,
     phone: "2125006035",
     last_sync: 1428328869,
     photos: [
    "https://irs3.4sqi.net/img/general/original/11402168_2zKtnTfWXPJJJAaX7N6g1EMPTR7ahNqSAOsMotN-jNU.jpg"
     ],
     local_id: 13778,
     likes: 0,
     city_id: 2621,
     address: "btwn Gansevoort & W 34th St",
     resource_uri: "/api/v1/venues/40f1d480f964a5206a0a1fe3/",
     id: "40f1d480f964a5206a0a1fe3",
     categories: [
     {
       name: "Park",
       parent: {
            local_id: 7,
            name_id: "sights",
            name: "Landmarks",
           id: "4d4b7105d754a06377d81259"
       },
       local_id: 494,
       name_id: "park",
       category_id: 7,
       id: "4bf58dd8d48988d163941735"
    }
   ],
   location: {
      lat: 40.7470618874989,
      lng: -74.0051937103271
   }
  },
  {
    rating: 0.97,
    name: "Central Park",
    ranking: 0,
    url: "http://www.centralparknyc.org",
    price: null,
    phone: "2123106600",
    last_sync: 1428521923,
    photos: [
  "https://irs2.4sqi.net/img/general/original/655018_Zp3vA90Sy4IIDApvfAo5KnDItoV0uEDZeST7bWT-qzk.jpg"
    ],
    local_id: 13826,
    likes: 0,
    city_id: 2621,
    address: "59th St to 110th St",
    resource_uri: "/api/v1/venues/412d2800f964a520df0c1fe3/",
    id: "412d2800f964a520df0c1fe3",
    categories: [
    {
       name: "Park",
       parent: {
          local_id: 7,
          name_id: "sights",
          name: "Landmarks",
          id: "4d4b7105d754a06377d81259"
       },
       local_id: 494,
       name_id: "park",
       category_id: 7,
       id: "4bf58dd8d48988d163941735"
     }
    ],
    location: {
      lat: 40.7888599444948,
      lng: -73.9611625671387
    }
   }
 ],
  meta: {
    total_count: 1344,
    next: "/api/v1/venues/?city_id=2621&category=topPicks&offset=2&limit=2&format=json",
    limit: 2,
    offset: 0
    }
}

这是对Retrofit服务的主要活动调用:

Map<String, String> params = new HashMap<String, String>();
    params.put("city_id", "2621");
    params.put("offset", "0");
    params.put("limit", "2"); 
    params.put("category", "topPicks");
    params.put("format", "json");


    ApiClient.getApiClient().listVenues(params, new Callback<List<ApiResponse>>() {

        @Override
        public void success(List<ApiResponse> venues, Response response) {

            //consumir venues
            Log.v("RETROFIT SUCCESS", response.getBody().toString());

            mAdapter = new MainCustomAdapter(venues);
            mRecyclerView.setAdapter(mAdapter);

        }

        @Override
        public void failure(RetrofitError retrofitError) {

            if (retrofitError.getResponse() != null) {
                Log.v("Retrofit error", retrofitError.getCause().toString());
            }
            //manejar el fallo


        }
    });

这是Api客户端:

public class ApiClient {
private static ApiVenuesInterface apiVenues;

public static ApiVenuesInterface getApiClient() {

   Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();

    if (apiVenues == null) {
        RestAdapter restAdapter = new RestAdapter.Builder()

                .setEndpoint("http://endpoint.com")
                .setConverter(new GsonConverter(gson))
                .setLogLevel(RestAdapter.LogLevel.FULL).setLog(new AndroidLog("RETROFIT"))
                .build();

        apiVenues = restAdapter.create(ApiVenuesInterface.class);
    }

    return apiVenues;
}

 public interface ApiVenuesInterface {
     //llamada asíncrona al querystring de venues
     @GET("/api/v1/venues")
     void listVenues(@QueryMap Map<String, String> params, Callback<List<ApiResponse>> callback);
}}

最后这是我的POJO模型(我认为是主要问题的地方):

public class ApiResponse {
    private List<Object> objects = new ArrayList<Object>();

}
class Object {

    @Expose
    private String name;
    @Expose
    private List<String> photos = new ArrayList<String>();
    @Expose
    private String address;
    @Expose
    private List<Category> categories = new ArrayList<Category>();

    /**
     *
     * @return
     * The name
     */
    public String getName() {
        return name;
    }


    /**
     *
     * @return
     * The photos
     */
    public List<String> getPhotos() {
        return photos;
    }

    /**
     *
     * @return
     * The address
     */
    public String getAddress() {
        return address;
    }

    /**
     *
     * @return
     * The categories
     */
    public List<Category> getCategories() {
        return categories;
    }

    class Category {
        @Expose
        private String name;

        /**
         *
         * @return
         * The name
         */
        public String getName() {
            return name;
        }

    }}

那么,我怎么必须建模我的POJO来解析我需要的数据呢?提前致谢 .

IMPORTANT EDIT: This question is valid for Retrofit 1.x. Be aware that Retrofit 2.x is a little bit different than this because it uses annotations and Call methods.

2 回答

  • 2

    按照之前的评论:

    基于快速查看,您的 Callback 返回类型不是 List<ApiResponse> 类型,而只是 ApiResponse . 如果您更改以下内容,应该开始工作:

    public interface ApiVenuesInterface {
         //llamada asíncrona al querystring de venues
         @GET("/api/v1/venues")
         void listVenues(@QueryMap Map<String, String> params, Callback<ApiResponse> callback);
    }
    

    目前,你告诉Gson将响应转换为 ApiResponse 对象列表,但实际上它只是包含一个列表/数组项目的单个 ApiResponse . 您正确地建模了POJO,但没有那么多改装回调 .


    [edit]回答你的后续问题:只需在 ApiResponse 类中添加一个getter即可访问包装对象:

    public List<Object> getObjects() {
        return objects;
    }
    

    一个小提示:为 Object 类提供一个不同的名称会很好,因为它的名称与Java中所有对象的'mother'相同: java.lang.Object . 这必然会导致混淆,并且很容易导入/引用错误 . 尝试提出一些更具描述性的东西,例如 Venue (因为看起来's what you'正在处理,尽管我可能会弄错) .


    [edit2] Gson生成一个 ApiResponse 对象,它包裹着 List<Object> . 迭代该列表可以像任何其他Java迭代一样完成;即使用增强的for循环:

    for (Object object : getObjects()) {
        // get the name for this object
        String name = object.getName();
        // get the address for this object
        String address = object.getAddress();
        // get all the photo urls for this object
        List<String> photos = object.getPhotos();
        // etc...
    }
    

    你看到 myjavapackage.Object@3c4a86e1 的原因是因为Java不知道如何将对象表示为打印字符串 . 您可以通过覆盖 Object 类中的以下方法来更改此表示形式:

    @Override public String toString() {
        // return a string representation for this object; i.e. its name:
        return name;
    }
    

    我仍然建议将 Object 类重命名为更合理的内容,以避免将其与内置的 java.lang.Object 混淆 .

  • -2

    尝试从json字符串中删除此部分

    {对象:

    最后一个花括号 .

相关问题