Gson 未使用 Retrofit 将 JSON 正确序列化为 POJO
Gson not properly serializing JSON into POJO with Retrofit
我有以下 JSON 从 NYTimes TopStories 返回 API;
{
"status": "OK",
"copyright": "Copyright (c) 2015 The New York Times Company. All Rights Reserved.",
"last_updated": "2015-07-03T01:25:02-05:00",
"num_results": 21,
"results": [
{
"section": "World",
"subsection": "Africa",
"title": "Tunisia Jihadist Died in Libya Strike, U.S. Official Says",
"abstract": "Seifallah Ben Hassine, one of Osama bin Laden’s top lieutenants and Tunisia’s most wanted jihadist, was killed in an American airstrike in Libya last month, a senior United States official said.",
"url": "http://www.nytimes.com/2015/07/03/world/africa/jihadist-from-tunisia-died-in-strike-in-libya-us-official-says.html",
"byline": "By CARLOTTA GALL and ERIC SCHMITT",
"item_type": "Article",
"updated_date": "2015-07-02T21:18:07-5:00",
"created_date": "2015-07-02T21:18:10-5:00",
"published_date": "2015-07-03T04:00:00-5:00",
"material_type_facet": "News",
"kicker": "",
"des_facet": [
"Terrorism",
"Defense and Military Forces"
],
"org_facet": "",
"per_facet": [
"Hassine, Seifallah Ben",
"bin Laden, Osama"
],
"geo_facet": [
"Tunisia",
"Libya"
],
"multimedia": ""
}
]
}
NYTimes returns 这个 JSON
的 header 为 text/json
,我不确定这是否对此有任何影响。
我正在使用 Retrofit,在回调中从未到达我的 onSuccess 方法。我的模型 classes 声明如下:
主模型Class
public class TopStories {
@SerializedName("status")
private String mStatus;
public String getStatus() {
return mStatus;
}
@SerializedName("copyright")
private String mCopyright;
public String getCopyright() {
return mCopyright;
}
@SerializedName("last_updated")
private String mLastUpdated;
public String getLastUpdated() {
return mLastUpdated;
}
@SerializedName("num_results")
private int mNumResults;
public int getNumResults() {
return mNumResults;
}
@SerializedName("results")
private List<Result> mResults;
public List<Result> getResults() {
return mResults;
}
}
结果模型Class
public class Result {
@SerializedName("section")
private String mSection;
public String getSection() {
return mSection;
}
@SerializedName("subsection")
private String mSubSection;
public String getSubSection() {
return mSubSection;
}
@SerializedName("title")
private String mTitle;
public String getTitle() {
return mTitle;
}
@SerializedName("abstract")
private String mAbstract;
public String getAbstract() {
return mAbstract;
}
@SerializedName("url")
private String mUrl;
public String getUrl() {
return mUrl;
}
@SerializedName("byline")
private String myByLine;
public String getMyByLine() {
return myByLine;
}
@SerializedName("item_type")
private String mItemType;
public String getItemType() {
return mItemType;
}
@SerializedName("updated_date")
private String mUpdatedDate;
public String getUpdatedDate() {
return mUpdatedDate;
}
@SerializedName("created_date")
private String mCreatedDate;
public String getCreatedDate() {
return mCreatedDate;
}
@SerializedName("multimedia")
private List<Multimedia> mMultimedia;
public List<Multimedia> getMultimedia() {
return mMultimedia;
}
}
你明白了,我有另一个模型 class 用于 "multimedia"
,好的,继续我创建了一个 RestAdapter
,如下所示:
private NYTimesService() {
mAsyncRestAdapter = new RestAdapter.Builder()
.setEndpoint(API_URL)
.setRequestInterceptor(new RequestInterceptor() {
@Override
public void intercept(RequestFacade request) {
request.addEncodedQueryParam("api-key", API_KEY);
}
})
.setLogLevel(RestAdapter.LogLevel.FULL)
.build();
}
我有一个像这样的 API 界面:
public interface ITopStories {
@Headers("Content-Type: text/json")
@GET("/topstories/v1/home.json")
void getTopStories(Callback<TopStories> callback);
}
我的 Callback<T>
是这样定义的:
@Subscribe
public void onLoadTopStories(LoadTopStories loadTopStories) {
Log.d(TAG, "onLoadTopStories");
Callback<TopStories> callback = new Callback<TopStories>() {
@Override
public void success(TopStories topStories, Response response) {
Log.d(TAG, "onSuccess");
mBus.post(new LoadedTopStories(topStories));
}
@Override
public void failure(RetrofitError error) {
}
};
sClient.getTopStories(callback);
}
Log.d(TAG, "onLoadTopStories");
被调用正常,问题是 Log.d(TAG, "onSuccess");
从未达到。这里有什么问题?
一些注意事项:
- "results" 可以是数组或字符串,如果它为空
- 与"multimedia"
相同
- 令我困惑的是
.getCopyright()
或 getStatus()
从未序列化为 TopStories.java
中的开头
另外 Retrofit
对 GET
请求没有问题并且它正确地启动了它:
更新
向 onFailure
添加了日志语句,我得到以下信息:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 1 column 10819 path $.results[4].multimedia
多媒体可以是数组或空字符串
"multimedia": [
{
"url": "http://static01.nyt.com/images/2015/07/04/world/04Greece1-web/04Greece1-web-thumbStandard.jpg",
"format": "Standard Thumbnail",
"height": 75,
"width": 75,
"type": "image",
"subtype": "photo",
"caption": "A Greek Orthodox priest giving money to a man on a street in Thessaloniki, Greece, on Friday.",
"copyright": "Giannis Papanikos/Associated Press"
},
{
"url": "http://static01.nyt.com/images/2015/07/04/world/04Greece1-web/04Greece1-web-thumbLarge.jpg",
"format": "thumbLarge",
"height": 150,
"width": 150,
"type": "image",
"subtype": "photo",
"caption": "A Greek Orthodox priest giving money to a man on a street in Thessaloniki, Greece, on Friday.",
"copyright": "Giannis Papanikos/Associated Press"
},
{
"url": "http://static01.nyt.com/images/2015/07/04/world/04Greece1-web/04Greece1-web-articleInline.jpg",
"format": "Normal",
"height": 127,
"width": 190,
"type": "image",
"subtype": "photo",
"caption": "A Greek Orthodox priest giving money to a man on a street in Thessaloniki, Greece, on Friday.",
"copyright": "Giannis Papanikos/Associated Press"
},
{
"url": "http://static01.nyt.com/images/2015/07/04/world/04Greece1-web/04Greece1-web-mediumThreeByTwo210.jpg",
"format": "mediumThreeByTwo210",
"height": 140,
"width": 210,
"type": "image",
"subtype": "photo",
"caption": "A Greek Orthodox priest giving money to a man on a street in Thessaloniki, Greece, on Friday.",
"copyright": "Giannis Papanikos/Associated Press"
}
]
您的 POJO 不正确,多媒体不是列表,它只是一个字符串,根据您的 JSON
好的,我能够通过创建扩展 Gson
的 JsonDeserializer<T>
class 的自定义反序列化 class 来实现这一点。以下代码对我有用:
public class ResultsDeserializerJson implements JsonDeserializer<Result> {
@Override
public Result deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonElement titleElement = json.getAsJsonObject().get("title");
JsonElement multimediaElement = json.getAsJsonObject().get("multimedia");
if (multimediaElement.isJsonArray()) {
return new Result(
titleElement.toString(),
(Multimedia[]) context.deserialize(multimediaElement.getAsJsonArray(), Multimedia[].class));
} else if (multimediaElement.getAsString().equals("")) {
Multimedia multimedia = new Multimedia();
multimedia.setFormat("");
multimedia.setUrl("");
return new Result(titleElement.toString(), multimedia);
} else {
Log.d("ResultsDeserializerJson", multimediaElement.toString());
throw new JsonParseException("Unsupported type of multimedia element");
}
}
}
我向 Result.java
添加了以下构造函数:
public Result(String title, Multimedia ... multimedia) {
this.mTitle = title.replace("\"", "");;
mMultimedia = Arrays.asList(multimedia);
}
我还是觉得这有点hacky,还有更好的解决办法。我会对此进行更多调查,但目前一切正常。您可以向 Multimedia
构造函数添加更多参数,但这在某些方面仍然不能完全满足我的要求。
这是我现在看到的结果:
应用程序的源代码在这里:NYTimes Android App Repo
我有以下 JSON 从 NYTimes TopStories 返回 API;
{
"status": "OK",
"copyright": "Copyright (c) 2015 The New York Times Company. All Rights Reserved.",
"last_updated": "2015-07-03T01:25:02-05:00",
"num_results": 21,
"results": [
{
"section": "World",
"subsection": "Africa",
"title": "Tunisia Jihadist Died in Libya Strike, U.S. Official Says",
"abstract": "Seifallah Ben Hassine, one of Osama bin Laden’s top lieutenants and Tunisia’s most wanted jihadist, was killed in an American airstrike in Libya last month, a senior United States official said.",
"url": "http://www.nytimes.com/2015/07/03/world/africa/jihadist-from-tunisia-died-in-strike-in-libya-us-official-says.html",
"byline": "By CARLOTTA GALL and ERIC SCHMITT",
"item_type": "Article",
"updated_date": "2015-07-02T21:18:07-5:00",
"created_date": "2015-07-02T21:18:10-5:00",
"published_date": "2015-07-03T04:00:00-5:00",
"material_type_facet": "News",
"kicker": "",
"des_facet": [
"Terrorism",
"Defense and Military Forces"
],
"org_facet": "",
"per_facet": [
"Hassine, Seifallah Ben",
"bin Laden, Osama"
],
"geo_facet": [
"Tunisia",
"Libya"
],
"multimedia": ""
}
]
}
NYTimes returns 这个 JSON
的 header 为 text/json
,我不确定这是否对此有任何影响。
我正在使用 Retrofit,在回调中从未到达我的 onSuccess 方法。我的模型 classes 声明如下:
主模型Class
public class TopStories {
@SerializedName("status")
private String mStatus;
public String getStatus() {
return mStatus;
}
@SerializedName("copyright")
private String mCopyright;
public String getCopyright() {
return mCopyright;
}
@SerializedName("last_updated")
private String mLastUpdated;
public String getLastUpdated() {
return mLastUpdated;
}
@SerializedName("num_results")
private int mNumResults;
public int getNumResults() {
return mNumResults;
}
@SerializedName("results")
private List<Result> mResults;
public List<Result> getResults() {
return mResults;
}
}
结果模型Class
public class Result {
@SerializedName("section")
private String mSection;
public String getSection() {
return mSection;
}
@SerializedName("subsection")
private String mSubSection;
public String getSubSection() {
return mSubSection;
}
@SerializedName("title")
private String mTitle;
public String getTitle() {
return mTitle;
}
@SerializedName("abstract")
private String mAbstract;
public String getAbstract() {
return mAbstract;
}
@SerializedName("url")
private String mUrl;
public String getUrl() {
return mUrl;
}
@SerializedName("byline")
private String myByLine;
public String getMyByLine() {
return myByLine;
}
@SerializedName("item_type")
private String mItemType;
public String getItemType() {
return mItemType;
}
@SerializedName("updated_date")
private String mUpdatedDate;
public String getUpdatedDate() {
return mUpdatedDate;
}
@SerializedName("created_date")
private String mCreatedDate;
public String getCreatedDate() {
return mCreatedDate;
}
@SerializedName("multimedia")
private List<Multimedia> mMultimedia;
public List<Multimedia> getMultimedia() {
return mMultimedia;
}
}
你明白了,我有另一个模型 class 用于 "multimedia"
,好的,继续我创建了一个 RestAdapter
,如下所示:
private NYTimesService() {
mAsyncRestAdapter = new RestAdapter.Builder()
.setEndpoint(API_URL)
.setRequestInterceptor(new RequestInterceptor() {
@Override
public void intercept(RequestFacade request) {
request.addEncodedQueryParam("api-key", API_KEY);
}
})
.setLogLevel(RestAdapter.LogLevel.FULL)
.build();
}
我有一个像这样的 API 界面:
public interface ITopStories {
@Headers("Content-Type: text/json")
@GET("/topstories/v1/home.json")
void getTopStories(Callback<TopStories> callback);
}
我的 Callback<T>
是这样定义的:
@Subscribe
public void onLoadTopStories(LoadTopStories loadTopStories) {
Log.d(TAG, "onLoadTopStories");
Callback<TopStories> callback = new Callback<TopStories>() {
@Override
public void success(TopStories topStories, Response response) {
Log.d(TAG, "onSuccess");
mBus.post(new LoadedTopStories(topStories));
}
@Override
public void failure(RetrofitError error) {
}
};
sClient.getTopStories(callback);
}
Log.d(TAG, "onLoadTopStories");
被调用正常,问题是 Log.d(TAG, "onSuccess");
从未达到。这里有什么问题?
一些注意事项:
- "results" 可以是数组或字符串,如果它为空
- 与"multimedia" 相同
- 令我困惑的是
.getCopyright()
或getStatus()
从未序列化为TopStories.java
中的开头
另外 Retrofit
对 GET
请求没有问题并且它正确地启动了它:
更新
向 onFailure
添加了日志语句,我得到以下信息:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 1 column 10819 path $.results[4].multimedia
多媒体可以是数组或空字符串
"multimedia": [
{
"url": "http://static01.nyt.com/images/2015/07/04/world/04Greece1-web/04Greece1-web-thumbStandard.jpg",
"format": "Standard Thumbnail",
"height": 75,
"width": 75,
"type": "image",
"subtype": "photo",
"caption": "A Greek Orthodox priest giving money to a man on a street in Thessaloniki, Greece, on Friday.",
"copyright": "Giannis Papanikos/Associated Press"
},
{
"url": "http://static01.nyt.com/images/2015/07/04/world/04Greece1-web/04Greece1-web-thumbLarge.jpg",
"format": "thumbLarge",
"height": 150,
"width": 150,
"type": "image",
"subtype": "photo",
"caption": "A Greek Orthodox priest giving money to a man on a street in Thessaloniki, Greece, on Friday.",
"copyright": "Giannis Papanikos/Associated Press"
},
{
"url": "http://static01.nyt.com/images/2015/07/04/world/04Greece1-web/04Greece1-web-articleInline.jpg",
"format": "Normal",
"height": 127,
"width": 190,
"type": "image",
"subtype": "photo",
"caption": "A Greek Orthodox priest giving money to a man on a street in Thessaloniki, Greece, on Friday.",
"copyright": "Giannis Papanikos/Associated Press"
},
{
"url": "http://static01.nyt.com/images/2015/07/04/world/04Greece1-web/04Greece1-web-mediumThreeByTwo210.jpg",
"format": "mediumThreeByTwo210",
"height": 140,
"width": 210,
"type": "image",
"subtype": "photo",
"caption": "A Greek Orthodox priest giving money to a man on a street in Thessaloniki, Greece, on Friday.",
"copyright": "Giannis Papanikos/Associated Press"
}
]
您的 POJO 不正确,多媒体不是列表,它只是一个字符串,根据您的 JSON
好的,我能够通过创建扩展 Gson
的 JsonDeserializer<T>
class 的自定义反序列化 class 来实现这一点。以下代码对我有用:
public class ResultsDeserializerJson implements JsonDeserializer<Result> {
@Override
public Result deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonElement titleElement = json.getAsJsonObject().get("title");
JsonElement multimediaElement = json.getAsJsonObject().get("multimedia");
if (multimediaElement.isJsonArray()) {
return new Result(
titleElement.toString(),
(Multimedia[]) context.deserialize(multimediaElement.getAsJsonArray(), Multimedia[].class));
} else if (multimediaElement.getAsString().equals("")) {
Multimedia multimedia = new Multimedia();
multimedia.setFormat("");
multimedia.setUrl("");
return new Result(titleElement.toString(), multimedia);
} else {
Log.d("ResultsDeserializerJson", multimediaElement.toString());
throw new JsonParseException("Unsupported type of multimedia element");
}
}
}
我向 Result.java
添加了以下构造函数:
public Result(String title, Multimedia ... multimedia) {
this.mTitle = title.replace("\"", "");;
mMultimedia = Arrays.asList(multimedia);
}
我还是觉得这有点hacky,还有更好的解决办法。我会对此进行更多调查,但目前一切正常。您可以向 Multimedia
构造函数添加更多参数,但这在某些方面仍然不能完全满足我的要求。
这是我现在看到的结果:
应用程序的源代码在这里:NYTimes Android App Repo