# Gson to Model containing several return types for one key

i just had a problem converting the JSON from Phillips Hue Lights (Response) to a Model. Since it may contain several "unknown" keys in success and/or may contain just a String for success i had to write/use a TypeAdapter and a deserializer aswell. This works but im pretty sure there is a better way to solve this.

Dummy JSONS:

[{"success":{"/config/name":"My bridge"}}]


and

[{
"success":{"id": "3"}
}]


and

[
]


And the JSON with just a String

[{
"success": "/config/whitelist/1234567890 deleted."
}]


It may also contain "error" but this isnt the problem. Since the object in address is unknown i use a Adapter from https://stackoverflow.com/questions/17627869/simple-way-to-strip-outer-array-of-responses-in-gson which works fine as long as its an Object. Since it may also contain a String i had to create a JsonDeserializer which validates if its a JSONObject and if not, it sets another variable in the Model with the message. It works, but i always have to revalidate if the message or success it set.

This is my JSONDeserializer

public class HueResponseDeserializer implements JsonDeserializer<HueResponse> {
private final String TAG = HueResponseDeserializer.class.getSimpleName();

@Override
public HueResponse deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context) throws JsonParseException {

HueResponse response = new HueResponse();
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject.get("success").isJsonObject()) {
for (Map.Entry<String, JsonElement> entry : jsonObject.getAsJsonObject("success").entrySet()) {
response.setSuccess(new HashMap<String, Object>() {{
put(entry.getKey(), entry.getValue());
}});
}
} else {
response.setSuccessMessage(jsonObject.get("success").getAsString());
}

if (jsonObject.has("error") && jsonObject.get("error").isJsonObject()) {
for (Map.Entry<String, JsonElement> entry : jsonObject.getAsJsonObject("error").entrySet()) {
response.setError(context.deserialize(entry.getValue(), HueResponse.Error.class));
}
}
return response;
}
}


And my Model

public class HueResponse {

private Map<String, Object> success;
private Error error;
private String successMessage;

public String getSuccessMessage() {
return successMessage;
}

public void setSuccessMessage(String successMessage) {
this.successMessage = successMessage;
}

public Map<String, Object> getSuccess() {
return success;
}

public Error getError() {
return error;
}

public void setSuccess(Map<String, Object> success) {
this.success = success;
}

public void setError(Error error) {
this.error = error;
}

public static class Error {
private int type;
private String description;

public int getType() {
return type;
}

}

public String getDescription() {
return description;
}

@Override
public String toString() {
return "Type: " + this.type
+ " Description: " + this.description;
}
}

}


### Iterating through an entrySet()

If what you really want is to put all the JSON objects into a success map, rather than overwriting each of them with every iteration, there's a way to stream it:

Map<String, Object> map = object.entrySet().stream()
.collect(Collectors.toMap(Entry::getKey,
entry -> (Object) entry.getValue()));
result.setSuccess(map);


In any case, a more fluent implementation of HashMap<>() {{ ... }} is to use Collections.singletonMap():

object.entrySet().forEach(entry -> {
Map<String, Object> map = Collections.singletonMap(entry.getKey(), entry.getValue());
// ...
});


### Modeling

You can consider have a more domain-based modeling of the successful results, instead of a simple Map<String, Object> in your HueResponse. For example, consider a Group of Action objects that have a state consisting of enabled (aka on), effect and hue? That seems to adhere closer to the results, giving you a more meaningful application.

• Thank you, but does it really solve the issue? If i see that correctly its just to use Lambdas which generates one more function (if not two) -> youtube.com/watch?v=WALV33rWye4&t=1600s. The issue/problem is that the return value of success may or may not a JSONObject. It may also just a String or anything else. – Emanuel S Feb 14 '17 at 11:37
• @EmanuelSeibold the first part addresses the double-brace initialization usage, which is unnecessary. The second is a suggestion on a richer model than just a mapping of JsonElement objects in your HueResponse class. – h.j.k. Feb 14 '17 at 13:40