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

How do I treat empty Strings as null objects with GSON?

我正在从 Reddit API 中检索评论。该模型是线程化的,因此每个评论都可以在内部拥有一个名为 replies 的评论列表。下面是 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" 的 CommentListing。

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

"replies":""

预期 BEGIN_OBJECT 但实际是 STRING

我试图在 CommentData class 上创建自定义反序列化器,但由于模型的递归性质,它似乎没有到达模型的底层。我想这是因为我正在使用一个单独的 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 return 一个 null 而不是一个字符串,这样它就不会试图将一个字符串强制到我的 POJO 中?或者,如果那不可能,请手动协调数据问题?如果您需要其他上下文或信息,请告诉我。谢谢

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

  • 您的类型适配器不应从外部捕获 Gson 实例。类型适配器工厂 (TypeAdapterFactory) 就是为此目的而设计的。此外,在 JSON 序列化器和反序列化器中,您可以分别通过 JsonSerializationContextJsonDeserializationContext 隐式引用它(这在某些情况下避免了无限递归)。
  • 尽可能避免修改JSON内存中的对象:序列化器和反序列化器只是一种管道,修改后的对象不会给您带来惊喜。
  • 您可以实现一个通用的 "empty string as a null" 类型的反序列化器并注释每个需要这种反序列化策略的 "bad" 字段。你可能会认为它很乏味,但它可以让你在任何需要的地方完全控制(我不知道 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;