Manually parse part of a response when using Retrofit - android

I'm working with a REST API that returns a JSON document that starts as follows and includes a "collection" of items with string IDs like "ABC". Note the "routes" field, which contains a series of fields called "ABC", "ABD", "ABE" etc, however routes is not represented as an array in the json, so all these
{
"status":true,
"page":1,
"per_page":500,
"total_count":1234,
"total_pages":8,
"total_on_page":500,
"routes":{
"ABC":[
{
"value":22313,
<......>
I'm using Retrofit and the problem is the routes field is not an array (despite the fact conceptually it certainly is) and Retrofit/Gson require me to create a model object for routes with field vars abc, abd, etc - this is not practical as the data changes. For various reasons changing the server API is hard, so I'm looking to work around this on the Android client.
I figure these are options:
Intercept the JSON document before it reaches Gson and tweak the document, possibly with a customised Gson parser, or by intercepting the HTTP response.
Bypass the JSON parsing, and acquire the JSON document from Retrofit (I've yet to figure out how to do this, or if it's possible)
Use some feature of Retrofit I'm unaware of to map field names to a collection.
I'd appreciate help, especially if there's a quick and easy way to resolve this.

It turns out that Retrofit's use of Gson by default makes it fairly easy to add a custom deserialiser to handle the portion of the JSON document that was the problem.
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(ApiDefinition.BASE_URL)
.setConverter(getGsonConverter())
.build();
public Converter getGsonConverter() {
Gson gson = new GsonBuilder()
.registerTypeAdapter(RouteList.class, new RouteTypeAdapter())
.create();
return new GsonConverter(gson);
}
public class RouteTypeAdapter implements JsonDeserializer<RouteList> {
#Override
public RouteList deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Gson gson = new Gson();
RouteList routeList = new RouteList();
JsonObject jsonObject = json.getAsJsonObject();
for (Map.Entry<String,JsonElement> elementJson : jsonObject.entrySet()){
RouteList wardsRoutes = gson.fromJson(elementJson.getValue().getAsJsonArray(), RouteList.class);
routeList.addAll(wardsRoutes);
}
return routeList;
}
}

After calling RestService, don't use Model Name as argument, you have to use Default Response class from retrofit library.
RestService Method
#FormUrlEncoded
#POST(GlobalVariables.LOGIN_URL)
void Login(#Field("email") String key, #Field("password") String value, Callback<Response> callback);
Calling method in Activity
getService().Login(email, password, new MyCallback<Response>(context, true, null)
{
#Override
public void failure(RetrofitError arg0)
{
// TODO Auto-generated method stub
UtilitySingleton.dismissDialog((BaseActivity<?>) context);
System.out.println(arg0.getResponse());
}
#Override
public void success(Response arg0, Response arg1)
{
String result = null;
StringBuilder sb = null;
InputStream is = null;
try
{
is = arg1.getBody().in();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null)
{
sb.append(line + "\n");
result = sb.toString();
System.out.println("Result :: " + result);
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
});

Related

Android - Facebook Graph API - not able to get AccessToken

So I'm trying to fetch Data from a Facebook page and in the Graph Explorer I'm doing just fine. I am using a global app Token.
I am able to fetch data here. So now I'm trying to do following:
GraphRequest request = GraphRequest.newGraphPathRequest(
accessToken,
"/<-pageid->/posts",
new GraphRequest.Callback() {
#Override
public void onCompleted(GraphResponse response) {
// Insert your code here
}
});
request.executeAsync();
}
But i just don't know how to create the Access Token as java Object.
AccessToken.getCurrentAccessToken();
is returning null in my case. I called following code in the beginning:
FacebookSdk.sdkInitialize(getApplicationContext());
AppEventsLogger.activateApp(this);
And did everything in the API Quickstart guide.
I tried to initialize a new Object of AccessToken, but I'm missing so many parameters and filling null didn't work out well. I just have an AppToken as String fetched as json from the graph api and a UserToken. How to achieve getting the AccessToken?
I got it working with a little workaround for everyone reading the question:
I didn't use the Facebook Android SDK, I just sent an GET-Request via my android app to fetch the json data and get it into my controller. I used following code:
public class FacebookController {
public static JSONObject readJsonFromUrl() throws IOException, JSONException {
InputStream is = new URL("https://graph.facebook.com/<page_id>/posts?limit=100&access_token=<my-access-token>").openStream();
try {
BufferedReader rd = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")));
String jsonText = readAll(rd);
JSONObject json = new JSONObject(jsonText);
return json;
} finally {
is.close();
}
}
private static String readAll(Reader rd) throws IOException {
StringBuilder sb = new StringBuilder();
int cp;
while ((cp = rd.read()) != -1) {
sb.append((char) cp);
}
return sb.toString();
}
Do not forget to wrap it in an AsyncTask when using it.

Retrofit Android Gson deserializer date inside json

I have the following problem:
The answers to my API are always like this "style":
{
"type": "dbUserResponse",
"user": {
"apellido": "canadas",
"email": "rj#canada.es",
"nacimiento": "1995-01-01T00:00:00+01:00",
}
}
That is, field "dbXXXXResponse" and model. That is, field "Response" and model. In this case user. Another example for the model Alert:
{
"type": "dbAlertResponse",
"alert": {
"alertId": 0,
"fecha": "2004-10-19T10:23:54+02:00",
}
}
Oki... What I need? Create a type Adapter deserialize or more?
One generic, or I have to make one typedapter for each model.
I have done the following:
public class GenericDeserializer<T> implements JsonDeserializer<T>
{
#Override
public T deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
throws JsonParseException
{
// Get the "content" element from the parsed JSON
JsonElement content = je.getAsJsonObject().get("type");
if(content.toString().equals("\"dbUserResponse\"")){
content = je.getAsJsonObject().get("user");
JsonObject jsonObject = content.getAsJsonObject();
if(jsonObject.has("nacimiento")){
String date = jsonObject.get("nacimiento").getAsString();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
Date date2 = null;
try {
date2 = sdf.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
String newstring = new SimpleDateFormat("yyyy-MM-dd").format(date2);
jsonObject.addProperty("nacimiento", newstring);
content = jsonObject.getAsJsonObject();
}
}
}
if(content.toString().equals("\"dbAlertResponse\"")){
// other case..... etc...
}
else{
//content = '{"error":"mierror"}';
}
// Deserialize it. You use a new instance of Gson to avoid infinite recursion
// to this deserializer
return new Gson().fromJson(content, type);
}
}
Thus, I have managed to successfully obtain the sub JSON "user" and fill my User model in Android.
But the "nacimiento" (date) field is not formatted :-(
I have also created a Singleton Retrofit, I'm not sure if it is correct:
public class SingletonRetrofit {
public static final String BASE_URL = "xxxxxxxxxxxxxxxxxxx";
private static Retrofit retrofit = null;
public static Retrofit getClient() {
Gson gson = new GsonBuilder()
.registerTypeAdapter(User.class, new GenericDeserializer<User>())
.create();
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
}
return retrofit;
}
}
My questions:
1º- How correctly format the date when the model is inside another Json Json like me?
2º- Will I have to create a typeadapter for each model? : -S
Thank you
Why don't you use POJO? Date in JSON is String so you must convert String to Date.

How to send a json to asp.net api Controller from android studio app?

I wrote an app where a user has to fill some text fields with infomation
like name, age ... When he clicks a button the data is being send to my api controller where the put methode saves the data in a data base.
This is kind of working exept that I don´t know how to send a json to my controller.(I researched several days but nothing worked)
So what I do at the moment is that I write an url conform string with my data
so that in my controller I read out of the string what I need to know.
This is a very bad solution!(I can´t do spaces or slashes and so on...)
To send my UserData I hava build an AsyncTask (When I didn´t I got an Exeption)
class SendUserTask extends AsyncTask<String, Void, String>{
public AsyncResponse delegate = null;
User user;
#TargetApi(Build.VERSION_CODES.KITKAT)
protected String doInBackground(String... urls) {
try {
String r = sendUser("name" + user.getName() + "street" + user.getStreet());
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
public void setUser(User u){user = u;}
public static String sendUser(String userData) throws Exception {
URL url = new URL("http://my-address:port/api/users/"+ userData);
HttpURLConnection c =(HttpURLConnection) url.openConnection();
c.setRequestMethod("PUT");
BufferedReader in = new BufferedReader(new InputStreamReader(
c.getInputStream()));
String inputLine;
String result = "";
while ((inputLine = in.readLine()) != null)
result = inputLine;
in.close();
return result;
}
}
this methode works fine when I just want to send an id or get something!
My controller is the defalt api controller
here is my put methode:
public String Put(string id, [FromBody]string value){
//stuff
}
Please can anyone help!?!
Try to consider this post. It describes how to send object or entity to server using retrofit. Also I have found topic that describes how to retrieve json data in server side.
Update
Android side
Try to create simple entity that would contain all data that you would like to send on server
Create simple interface that would be send data to server. Consider next:
public interface UserApi {
#PUT("/api/users/")
Call<Object> sendData(#Body FooRequest body);
}
Get adapter end send request
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://my-address:port")
.build();
UserApi service = retrofit.create(UserApi.class)
service.sendData(new FooRequest());
Server side
Try create apicontroller like this (I`m not sure)
public class UtilityController : ApiController
{
[HttpPut]
public string Put(FooRequest fooRequest)
{
return "bla";
}
}
I hope it would help you!
here is my test project
Good Luck

How do I convert a successful Response body to a specific type using retrofit?

In async mode retrofit calls
public void success(T t, Response rawResponse)
were t is the converted response, and rawResponse is the raw response. This provides you with access to both the raw response and the converted response.
In sync mode you can get either the converted response OR the raw response
converted response
#GET("/users/list")
List<User> userList();
raw response
#GET("/users/list")
Response userList();
The Response object does have a method to get the body
TypedInput getBody()
and the retrofit api does have a converter class that can convert this to a java object
Object fromBody(TypedInput body,Type type)
But I can not figure out how to get an instance of the Converter object
I might be able to create an instance of the Converter class, but that would require knowledge of the Gson object used to create the RestAdapter, which I may not have access to. Ideally, I would like obtain a reference to the converter object directly the RestAdpater.
Any of the following will answer my question:
Is there a way to get a reference to the default Converter used by retrofit?
Does anyone know how the default Converter is constructed? (there is no default constructor and there are two Constructors public GsonConverter(Gson gson) and public GsonConverter(Gson gson, String charset)
Is there any other way to get both the raw and converted response in sync mode?
Here's an example of a StringConverter class that implements the Converter found in retrofit. Basically you'll have to override the fromBody() and tell it what you want.
public class StringConverter implements Converter {
/*
* In default cases Retrofit calls on GSON which expects a JSON which gives
* us the following error, com.google.gson.JsonSyntaxException:
* java.lang.IllegalStateException: Expected BEGIN_OBJECT but was
* BEGIN_ARRAY at line x column x
*/
#Override
public Object fromBody(TypedInput typedInput, Type type)
throws ConversionException {
String text = null;
try {
text = fromStream(typedInput.in());
} catch (IOException e) {
e.printStackTrace();
}
return text;
}
#Override
public TypedOutput toBody(Object o) {
return null;
}
// Custom method to convert stream from request to string
public static String fromStream(InputStream in) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder out = new StringBuilder();
String newLine = System.getProperty("line.separator");
String line;
while ((line = reader.readLine()) != null) {
out.append(line);
out.append(newLine);
}
return out.toString();
}
}
Applying this to your request you'll have to do the following:
// initializing Retrofit's rest adapter
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(ApiConstants.MAIN_URL).setLogLevel(LogLevel.FULL)
.setConverter(new StringConverter()).build();

Convert JSON String to Dictionary in Android

I have the following json formatted string that is returned from the web service:
{"Success":false,"Message":"This version is not supported"}
I am using the following code to invoke the web service:
AsyncHttpClient client = new AsyncHttpClient();
client.get("http://mywebsite/check/getcompatibilityinfo", new AsyncHttpResponseHandler() {
#Override
public void onSuccess(String response) {
System.out.println(response);
}
});
The response contains the json string now I need to access the Success and the Message property. Is there any simple way to do it without using complicated third party libraries?
The JSONObject class is already available in your Android codebase (no 3rd party dependencies). Since your example uses normal (simple) JSON, you can use:
try {
JSONObject responseJSON = new JSONObject(response);
} catch (JSONException e) {
e.printStackTrace();
}
boolean success = responseJSON.getBoolean("Success");
String message = responseJSON.getString("Message");

Categories

Resources