Say I have this API that:
- requires a lot of authentication things.
- always returns JSON, but sometimes an object, and sometimes an array. It's however, easy to predict what will be returned.
And I want to use this super freaking awesome Ion library (by Koushik Dutta).
As the API I'm using, requires authentication, setting proper headers with every request, etc. I'm going to wrap it somehow, say: http://goo.gl/5NLeQn
private void sendRequest(String action, JsonObject params, FutureCallback<JsonObject> callback) {
String nonce = "" + getNonce();
Builders.Any.B req = Ion.with(context, BASE_URL + action);
req.setBodyParameter("key", API_KEY)
.setBodyParameter("signature", getSignature(nonce))
.setBodyParameter("nonce", nonce);
if( params!=null ) {
for(Map.Entry<String, JsonElement> param : params.entrySet()) {
JsonElement el = param.getValue();
if( el.isJsonPrimitive() ) req.setBodyParameter(param.getKey(), el.getAsString());
}
}
req.asJsonObject()
.setCallback(callback);
}
That works great until I need to receive a request that instead of being a JsonObject
is a JsonArray
:
java.lang.ClassCastException: com.google.gson.JsonArray cannot be cast to com.google.gson.JsonObject
So as a quick workaround I can create two methods that redirect flow to one common, but to make things easier here, say it looks like this: http://goo.gl/pzSal3 .
private void sendRequest(String action, JsonObject params, FutureCallback<JsonObject> callback) {
String nonce = "" + getNonce();
Builders.Any.B req = Ion.with(context, BASE_URL + action);
req.setBodyParameter("key", API_KEY)
.setBodyParameter("signature", getSignature(nonce))
.setBodyParameter("nonce", nonce);
if( params!=null ) {
for(Map.Entry<String, JsonElement> param : params.entrySet()) {
JsonElement el = param.getValue();
if( el.isJsonPrimitive() ) req.setBodyParameter(param.getKey(), el.getAsString());
}
}
req.asJsonObject()
.setCallback(callback);
}
private void sendRequest(String action, JsonObject params, FutureCallback<JsonArray> callback) {
String nonce = "" + getNonce();
Builders.Any.B req = Ion.with(context, BASE_URL + action);
req.setBodyParameter("key", API_KEY)
.setBodyParameter("signature", getSignature(nonce))
.setBodyParameter("nonce", nonce);
if( params!=null ) {
for(Map.Entry<String, JsonElement> param : params.entrySet()) {
JsonElement el = param.getValue();
if( el.isJsonPrimitive() ) req.setBodyParameter(param.getKey(), el.getAsString());
}
}
req.asJsonArray()
.setCallback(callback);
}
But then BAM, another error appears:
'sendRequest(String, JsonObject, FutureCallback)' clashes with 'sendRequest(String, JsonObject, FutureCallback)'; both methods have same erasure
Seems like those types got stripped down in runtime and VM gets confused.
I've come up with some "solutions":
- I could use
FutureRequest
without declaring types at all, but then I need them in #18th line. - I could use
String
instead, and then parse it withGson
, - I could use another param to specify type, and then cast my
callback
to eitherFutureRequest<JsonObject>
orFutureRequest<JsonArray>
But all of them seem to be only some cheap hacks to make things (somehow) work. Does anyone here know any proper solution to that problem?
My most preferred way of calling those methods would be:
sendRequest(ACTION_BALANCE, null, new FutureRequest<JsonObject>() {
@Override
public void onCompleted(Exception e, JsonObject result) {
// my code goes here
}
});
// OR just
sendRequest(ACTION_OPERATIONS, null, new FutureRequest<JsonArray>() {
@Override
public void onCompleted(Exception e, JsonArray result) {
// my code goes here
}
});
Instead of
FutureCallback<JsonObject>
andFutureCallback<JsonArray>
, why not extend these like this:Then you can call your methods like so: