Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Retrofit and GSON: JSON response returns object or string as property value

The JSON data returned from the server can either return an object, or if that object is null, it returns an empty string ("").

My problem is that my DTO expects an object, but it sees a string and crashes.

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
)

Different responses from server

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

...

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

I've heard that I might need a custom GSON deserializer, but I've never done anything with GSON other than the out of the box stuff - so I was hoping for a nudge in the right direction.

Thanks!

like image 976
Josh Beckwith Avatar asked Sep 20 '25 19:09

Josh Beckwith


1 Answers

Easiest hack is that you can add extra fields in the class with the same serialised name but with a String data type. Like this -

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

As there is nothing in Gson as "Required" field, you'll just get a null in your deserialized object if something is missing in the JSON. So, if there is an empty string the FavoriteDto object will be null and not null otherwise.

EDIT

I'm adding some Java code that I have written earlier. This might help:

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;
    }

Defining the 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());
            }
        }
    }

And:

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

Lastly:

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();
like image 114
Subrata Mondal Avatar answered Sep 22 '25 09:09

Subrata Mondal