如何在 Retrofit GET 请求后使用 GSON 解析此 JSON 响应?
How to parse this JSON response with GSON after a Retrofit GET request?
我正在尝试在 Retrofit GET 请求后使用 GSON 解析 JSON 响应。我不需要所有的键和值,所以我只 @Expose 我需要的那些,并指示解析器这样做。请求触发正常,响应干净利落,但查看 logcat 我发现了这个错误,这显然表明 POJO 模型的格式或实现不正确:
04-09 12:16:01.679 5604-5604/? V/Retrofit 错误:retrofit.converter.ConversionException: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: 预期 BEGIN_ARRAY 但在第 1 行第 2 列路径 $ BEGIN_OBJECT
这是 JSON 响应(在 GET 请求后 returns 正常):
{
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 服务的主要 activity 调用:
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 才能解析我需要的数据?提前致谢。
重要编辑:这个问题对 Retrofit 1.x 有效。请注意,Retrofit 2.x 与此略有不同,因为它使用注释和 Call 方法。
尝试从 json 字符串中删除此部分
{
objects:
和最后一个花括号。
根据之前的评论:
根据快速查看,您的 Callback
return 类型不是 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
包装 list/array 个项目.您正确地为 POJO 建模,但没有那么多改造回调。
[edit] 回答您的后续问题:您只需将 getter 添加到 ApiResponse
class:
即可访问包装对象
public List<Object> getObjects() {
return objects;
}
一个小提示:最好为您的 Object
class 取一个不同的名称,因为它的名称与 'mother' 中所有对象的名称相同=50=]:java.lang.Object
。这势必会导致混乱,并且很容易出现 importing/referencing 错误。试着想出一些更具描述性的东西,例如 Venue
(看起来这就是你正在处理的内容,尽管我可能弄错了)。
[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
class:
中的以下方法来更改此表示
@Override public String toString() {
// return a string representation for this object; i.e. its name:
return name;
}
我仍然建议将 Object
class 重命名为更合理的名称,以免与内置的 java.lang.Object
.
混淆
我正在尝试在 Retrofit GET 请求后使用 GSON 解析 JSON 响应。我不需要所有的键和值,所以我只 @Expose 我需要的那些,并指示解析器这样做。请求触发正常,响应干净利落,但查看 logcat 我发现了这个错误,这显然表明 POJO 模型的格式或实现不正确:
04-09 12:16:01.679 5604-5604/? V/Retrofit 错误:retrofit.converter.ConversionException: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: 预期 BEGIN_ARRAY 但在第 1 行第 2 列路径 $ BEGIN_OBJECT
这是 JSON 响应(在 GET 请求后 returns 正常):
{
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 服务的主要 activity 调用:
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 才能解析我需要的数据?提前致谢。
重要编辑:这个问题对 Retrofit 1.x 有效。请注意,Retrofit 2.x 与此略有不同,因为它使用注释和 Call 方法。
尝试从 json 字符串中删除此部分
{ objects:
和最后一个花括号。
根据之前的评论:
根据快速查看,您的 Callback
return 类型不是 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
包装 list/array 个项目.您正确地为 POJO 建模,但没有那么多改造回调。
[edit] 回答您的后续问题:您只需将 getter 添加到 ApiResponse
class:
public List<Object> getObjects() {
return objects;
}
一个小提示:最好为您的 Object
class 取一个不同的名称,因为它的名称与 'mother' 中所有对象的名称相同=50=]:java.lang.Object
。这势必会导致混乱,并且很容易出现 importing/referencing 错误。试着想出一些更具描述性的东西,例如 Venue
(看起来这就是你正在处理的内容,尽管我可能弄错了)。
[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
class:
@Override public String toString() {
// return a string representation for this object; i.e. its name:
return name;
}
我仍然建议将 Object
class 重命名为更合理的名称,以免与内置的 java.lang.Object
.