【问题标题】:Android Retrofit and GSON: JSON response returns object or string as property valueAndroid Retrofit 和 GSON:JSON 响应返回对象或字符串作为属性值
【发布时间】:2018-06-15 15:41:45
【问题描述】:

从服务器返回的 JSON 数据可以返回一个对象,或者如果该对象为空,则返回一个空字符串 ("")。

我的问题是我的 DTO 需要一个对象,但它看到一个字符串并崩溃。

PersonDTO

data class PersonDto(
    @SerializedName("firstName") val first: String,
    @SerializedName("lastName") val last: String,
    @SerializedName("favorites") val favorites: FavoriteDto,
)

FavoriteDto

class FavoriteDto(
    @SerializedName("color") val color: String,
    @SerializedName("number") val number: Int
)

来自服务器的不同响应

"person" : {
    "firstName": "Steve",
    "lastName" : "Johnson",
    "favorites" : {
        "color": "Purple",
        "number": 25
    }
}

...

"person" : {
    "firstName": "Steve",
    "lastName" : "Johnson",
    "favorites" : ""
}

我听说我可能需要一个自定义 GSON 解串器,但除了开箱即用的东西之外,我从未对 GSON 做过任何事情 - 所以我希望能朝着正确的方向推动。

谢谢!

【问题讨论】:

  • 这是您控制的服务器吗?如果收藏夹是一个对象,在一种情况下它不应该是一个对象,而在另一种情况下应该被视为一个字符串
  • 不幸的是,这不是我的服务器。我必须处理这个客户端。
  • 发布您的错误堆栈跟踪。

标签: android kotlin gson retrofit


【解决方案1】:

最简单的技巧是您可以在类中添加具有相同序列化名称但具有字符串数据类型的额外字段。像这样-

data class PersonDto(
    @SerializedName("firstName") val first: String,
    @SerializedName("lastName") val last: String,
    @SerializedName("favorites") val favorites: FavoriteDto,
    @SerializedName("favorites") val favoritesStr: String,
)

由于 Gson 中没有任何内容作为“必需”字段,因此如果 JSON 中缺少某些内容,您只会在反序列化对象中获得 null。因此,如果有一个空字符串,FavoriteDto 对象将为空,否则不为空。

编辑

我正在添加一些我之前编写的 Java 代码。这可能会有所帮助:

public class PersonDto {
    private FavoriteDto favorites;
    private String favoritesStr;
    public FavoriteDto getResponseDataObject() {
        return favorites;
    }
    public void setResponseDataObject(FavoriteDto favorites) {
        this.favorites = favorites;
    }
    public String getResponseDataString() {
        return favoritesStr;
    }
    public void setResponseDataString(String favoritesStr) {
        this.favoritesStr = favoritesStr;
    }

定义反序列化器:

public static class ArrayObjectDualityDeserializer implements JsonDeserializer<PersonDto> {

        public PersonDto deserialize(JsonElement json, Type typeOfT,
                                         JsonDeserializationContext context) throws JsonParseException {
            PersonDto response = new PersonDto();
            JsonObject object = json.getAsJsonObject();
            if(object.get("favorites").isJsonArray()) {

            } else if(object.get("favorites").isJsonObject()) {
                try {
                    FavoriteDto dtoObject = gson.fromJson(object.get("favorites"), FavoriteDto.class);
                    response.setResponseDataObject(dtoObject);
                } catch (JsonSyntaxException e) {
                    DebugLogger.e("Error " + e);
                }
            }  else if (object.get("favorites").isJsonNull()) {

            } else {
                response.setResponseDataString(object.get("favorites").getAsString());
            }
        }
    }

还有:

public static Gson gson = new GsonBuilder()
            .registerTypeAdapter(PersonDto.class, new ArrayObjectDualityDeserializer())
            .create();

最后:

private static Retrofit retrofit = null;
    private static OkHttpClient.Builder httpClient = null;
    private static OkHttpClient session_client = null;
httpClient = new OkHttpClient.Builder();
            httpClient.addInterceptor(new SessionOkHttpInterceptor());
            session_client = httpClient.build();
            retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .client(session_client)
                    .addConverterFactory(GsonConverterFactory.create(gson))
                    .build();

【讨论】:

  • 我认为无论如何它仍然会尝试将收藏夹作为对象提取出来。无论如何尝试这个,我得到了错误 - PersonDto 声明了多个名为收藏夹的 JSON 字段
【解决方案2】:

@Subrato M. 的答案部分正确。为了工作,您应该从两个字段中删除 @SerializedName("favorites") 注释以便工作。在这种情况下,Gson 在反序列化多个名为 favorites 的 json 字段时不会抛出错误,因为您没有任何使用该名称注释的字段(也不要将字段命名为与预期字段名称相同的名称,因为 @987654324 @ 将尝试反序列化)。使用自定义反序列化器检查是对象还是字符串。

【讨论】:

    【解决方案3】:

    现在不需要@Subrato M 的hack,你没有得到正确的json,因为你在class FavoriteDto 之前缺少data 关键字,这就是为什么不存在get 和set 方法

    【讨论】:

      猜你喜欢
      • 2012-02-21
      • 2018-11-22
      • 2017-11-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-17
      • 2010-10-22
      • 1970-01-01
      相关资源
      最近更新 更多