Android Retrofit和GSON:JSON响应将对象或字符串作为属性值返回

时间:2018-01-05 19:34:40

标签: android kotlin gson retrofit

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

我的问题是我的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做任何事情 - 所以我希望能朝着正确的方向轻推。

谢谢!

3 个答案:

答案 0 :(得分:3)

最简单的黑客是你可以在类中添加具有相同序列化名称但具有String数据类型的额外字段。像这样 -

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对象将为null,否则为null。

修改

我正在添加一些我之前编写过的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;
    }

定义Deserializar:

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();

答案 1 :(得分:0)

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

答案 2 :(得分:0)

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