如何使用GSON将空字符串视为空对象?

时间:2018-02-15 05:12:22

标签: java json gson reddit

我正在从Reddit API中检索评论。该模型具有线程,因此每个Comment都可以在内部有一个注释列表,名为回复。以下是JSON响应的外观示例:

[
   {
      "kind":"Listing",
      "data":{
         "children":[
            {
               "data":{
                  "body":"comment",
                  "replies":{
                     "kind":"Listing",
                     "data":{
                        "children":[
                           {
                              "data":{
                                 "body":"reply to comment",
                                 "replies":""
                              }
                           }
                        ]
                     }
                  }
               }
            }
         ]
      }
   }
]

以下是我使用POJO进行模拟的方法。上面的回复将被视为CommentListings列表。

public class CommentListing {
    @SerializedName("data")
    private CommentListingData data;
}

public final class CommentListingData {
    @SerializedName("children")
    private List<Comment> comments;
}

public class Comment {
    @SerializedName("data")
    private CommentData data;
}

public class CommentData {
    @SerializedName("body")
    private String body;

    @SerializedName("replies")
    private CommentListing replies;
}

注意底层CommentData POJO如何引用另一个名为“replies”的CommentList。

此模型有效,直到GSON到达最后一个没有回复的子CommentData。 API不提供null,而是提供空字符串。当然,这会导致GSON异常,它需要一个对象但却找到一个String:

"replies":""

预计BEGIN_OBJECT但是STRING

我试图在CommentData类上创建自定义反序列化器,但由于模型的递归性质,它似乎没有达到模型的底层。我想这是因为我使用单独的GSON实例来完成反序列化。

@Singleton
@Provides
Gson provideGson() {
    Gson gson = new Gson();
    return new GsonBuilder()
            .registerTypeAdapter(CommentData.class, new JsonDeserializer<CommentData>() {
                @Override
                public CommentData deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
                    JsonObject commentDataJsonObj = json.getAsJsonObject();
                    JsonElement repliesJsonObj = commentDataJsonObj.get("replies");
                    if (repliesJsonObj != null && repliesJsonObj.isJsonPrimitive()) {
                        commentDataJsonObj.remove("replies");
                    }

                    return gson.fromJson(commentDataJsonObj, CommentData.class);
                }
            })
            .serializeNulls()
            .create();
}

如何强制GSON返回null而不是String,以便它不会尝试强制String进入我的POJO?或者,如果不可能,手动协调数据问题?如果您需要其他背景信息,请与我们联系。感谢。

1 个答案:

答案 0 :(得分:8)

一般来说,你的代码看起来不错,但我会推荐一些东西:

  • 您的类型适配器不应从外部捕获Gson个实例。类型适配器工厂(TypeAdapterFactory)是为此目的而设计的。此外,在JSON序列化器和反序列化器中,您可以分别通过JsonSerializationContextJsonDeserializationContext隐式引用它(这可以避免在某些情况下无限递归)。
  • 尽量避免在内存中修改JSON对象:序列化程序和反序列化程序只是一种管道,不应该为修改后的对象带来意外。
  • 您可以将通用&#34;空字符串实现为null&#34;键入反序列化器并注释每个&#34;坏&#34;需要这种反序列化策略的领域。您可能认为它很乏味,但它可以让您完全控制您需要它的地方(我不知道Reddit API是否有更多这样的怪癖)。
public final class EmptyStringAsNullTypeAdapter<T>
        implements JsonDeserializer<T> {

    // Let Gson instantiate it itself
    private EmptyStringAsNullTypeAdapter() {
    }

    @Override
    public T deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context)
            throws JsonParseException {
        if ( jsonElement.isJsonPrimitive() ) {
            final JsonPrimitive jsonPrimitive = jsonElement.getAsJsonPrimitive();
            if ( jsonPrimitive.isString() && jsonPrimitive.getAsString().isEmpty() ) {
                return null;
            }
        }
        return context.deserialize(jsonElement, type);
    }

}

然后只需注释replies字段:

@SerializedName("replies")
@JsonAdapter(EmptyStringAsNullTypeAdapter.class)
private CommentListing replies;