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

Ste*_*eve 3 java json reddit gson

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

[
   {
      "kind":"Listing",
      "data":{
         "children":[
            {
               "data":{
                  "body":"comment",
                  "replies":{
                     "kind":"Listing",
                     "data":{
                        "children":[
                           {
                              "data":{
                                 "body":"reply to comment",
                                 "replies":""
                              }
                           }
                        ]
                     }
                  }
               }
            }
         ]
      }
   }
]
Run Code Online (Sandbox Code Playgroud)

以下是我使用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;
}
Run Code Online (Sandbox Code Playgroud)

注意底层CommentData POJO如何引用另一个名为"replies"的CommentList.

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

"replies":""
Run Code Online (Sandbox Code Playgroud)

预计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();
}
Run Code Online (Sandbox Code Playgroud)

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

Lyu*_*riv 9

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

  • 您的类型适配器不应Gson从外部捕获实例.类型适配器工厂(TypeAdapterFactory)是为此目的而设计的.此外,在JSON序列化和反序列化,你可以通过隐指之JsonSerializationContextJsonDeserializationContext分别为(这避免了在某些情况下无限递归).
  • 尽量避免在内存中修改JSON对象:序列化程序和反序列化程序只是一种管道,不应该带来修改对象的意外.
  • 您可以实现泛型"空字符串作为null"类型的反序列化器,并注释需要这种反序列化策略的每个"坏"字段.您可能认为这很乏味,但它可以让您完全控制您需要它的地方(我不知道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);
    }

}
Run Code Online (Sandbox Code Playgroud)

然后只需注释该replies字段:

@SerializedName("replies")
@JsonAdapter(EmptyStringAsNullTypeAdapter.class)
private CommentListing replies;
Run Code Online (Sandbox Code Playgroud)