1

I am trying to learn about Retrofit since it seems to take care of a lot of the issues I am currently having with JSON requests and handling.

first and foremost, I understand that the methods we use are defined inside of interfaces, while making simple requests to obtain data it is quite simple to specify what is to be retrieved from the url as well as all the necessary endpoints based on the famous github example.

So if we are retrieving information form the github api, we would first create all the necessary pojo models and such and then define the interface as:

public interface GithubService {
    @GET("users/{username}")
    Observable<Github>getGithHubUser(@Path("username")String userName);
}

From that on the main activity we would have something like:

Retrofit retrofit = new Retrofit.Builder()
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .baseUrl("https://api.github.com/")
                .build();

        GithubService githubService = retrofit.create(GithubService.class);
        Observable<Github> githubUser = githubService.getGithHubUser("usersName");
        githubUser.subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .map(user -> "Github Username: " + user.getName() + "\nUrl:" +user.getUrl() + "\nfollowing: "+ user.getHireable())
                .subscribe(userInfo -> Log.d("Output", userInfo));

My question here would be how to send JSON information if the url requires something like this:

"data={\"process\":\"procesNumber\", \"phone\":\"123456\"}"

Basically, in order to get any response form the server I have been doing this using simple okhttp:

OkHttpClient client = new OkHttpClient();
        RequestBody body = RequestBody.create(CREATE_MEDIA_TYPE, "data={\"process\":\"procesNumber\", \"phone\":\"123456\"}");
        String ALLWAYS_API = "http://something something bla bla";
        Request request = new Request.Builder()
                .url("https://blablabla")
                .post(body)
                .build();

        Call call = client.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                    ... etc etc etc
            }   

To my understanding, even I need to create a pojo class that represents the data that needs to be sent to retrofit, something along the lines of:

public class DataRequest {
    final String proces;
    final String phone;

    DataRequest(String process, String phone) {
        this.process = process;
        this.phone = phone;

    }
}

Which would comply to the information being sent to the request, but how would I actually parse that to the interface implementation?

interface DataService {
    @Post(not a clue what to place here)
    DataRequest postJson(@Body how?)

}

And how would I actually add that to the retrofit builder? The examples that I am using come from different forums on the web as well as other questions asked by other users, this one in particular helped a lot in understanding a couple of things: How to POST raw whole JSON in the body of a Retrofit request? but I still don't understand where everything goes and some of the other questions and examples are far too complex for what I need to do.

Community
  • 1
  • 1
sigillum_conf
  • 423
  • 3
  • 22
  • if this is really what your api expects, change your api. – njzk2 Nov 28 '16 at 22:28
  • @njzk2 man I wish I could.....I wish..I...could...... – sigillum_conf Nov 28 '16 at 22:32
  • The first thing is that you need to use the `@Field` annotation to send something in the shape `data=...` – njzk2 Nov 28 '16 at 22:45
  • The `@Post` parameter contains, as pretty clearly indicated in the doc, the path to your endpoint. – njzk2 Nov 28 '16 at 22:46
  • I you try `@Field DataRequest data`. I am not sure how objects are serialized for `Field`, though, so you may have to look into that – njzk2 Nov 28 '16 at 22:47
  • @njzk2 btw, is that not standard? I understand that to get a particular server response when it comes to post requests, that in certain scenarios some information needs to be sent. In this case it is process and phone, is passing information in this way not the standard? or is receiving the information in such way what made you say that? To me it feels funny having to send the information that way. As in, passing a json body to a url is not standard. Am i correct in viewing it this way? – sigillum_conf Nov 28 '16 at 22:47
  • it is indeed not standard. In most cases you'll have either just the json as the body (without the `data=` part first), or each individual value as `process=procesNumber&phone=123456` in url encoded form data. mixing both is non-standard, but still relatively common – njzk2 Nov 28 '16 at 22:49
  • @njzk2 which has been an uphill struggle. Every tutorial or doc I find has the required code for doing it the way you just mentioned. I have been jumping through a lot of hoops just to get the information to work as it currently stands. – sigillum_conf Nov 28 '16 at 22:55
  • No takers I guess – sigillum_conf Nov 29 '16 at 03:09
  • so did you try to declare your parameter as `@Field DataRequest data` ? – njzk2 Nov 29 '16 at 15:04
  • @njzk2 Actually, I got the code to work using something different, since I am posting a string which looks like json(because it is what the API expects......don´t judge me for that, i did not build it) I had to use a different converter factory. Now I get the response with the information, but there is some html thrown in there which is currently tripping me out, if I can't figure that out it will be posted as a separate question – sigillum_conf Nov 29 '16 at 20:28
  • If you found a solution to your problem, please post it as an answer! – njzk2 Nov 29 '16 at 21:50
  • @njzk2 just added the answer down, i hope this helps someone in the future, it took me a little bit to get the hang of it, but it was definitely worth it, i can definitely see the benefit of using retrofit, in contrast to what i was using before(plain okhttp) – sigillum_conf Nov 30 '16 at 18:56

1 Answers1

1

Ok, so in order to leave an answer here for anyone trying to do this. By default, retrofit comes with many utilities which handle the passing of data as JSON, but in this case what I am passing is a string that looks like json inside of a tag called data......I know..

But in order to answer this for the people facing similar issues, in order to pass in the string we need to import a scalar convertor much in the same way that we need to import a gson converter to work with our retrofit services:

compile  'com.squareup.retrofit2:converter-scalars:2.0.2'

After that, our service can be handled as:

public interface CreateService {
    @Headers({ "Content-Type: application/x-www-form-urlencoded;charset=UTF-8"})
    @POST("your/post/path/goes/here")
    Call<String> getStringScalar(@Body String body);
}

I write my service generators in a separate file, in this case, the whole thing can be used in this way:

public class ServiceGeneratorWithScalarConvertor {
    private static final String API_BASE_URL = "your/base/url/goes/here";

    private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();

    // basically, this code is the same as the one from before with the added instance of creating and making use of the scalar converter factory.....scratch that i took it off
    private static Retrofit.Builder builder =
            new Retrofit.Builder()
                    .baseUrl(API_BASE_URL)
                    .addConverterFactory(ScalarsConverterFactory.create())
                    .addConverterFactory(GsonConverterFactory.create());



    public static <S> S createService(Class<S> serviceClass) {
        builder.client(httpClient.build());
        Retrofit retrofit = builder.build();
        return retrofit.create(serviceClass);
    }
}

From there, we can access the results with this particular method(i am using this method inside my main activity:

public void retroFitCreateAPIExample() {


        CreateService service = ServiceGeneratorWithScalarConvertor.createService(CreateService.class);
        String body = "data={\"process\":\"process1\",\"phone\":\"12345\"}";
        Call<String> call = service.getStringScalar(body);
        call.enqueue(new Callback<String>() {
            @Override
            public void onResponse(Call<String> call, Response<String> response) {
                if(response.isSuccessful()){
                    Log.d("Response Body>>>>>", response.body());
                    createR = new Gson().fromJson(response.body().toString(), CreateModels.class);
                    Log.d("CREATED RESPONSE",createR.getCreate().getStops().get(0).getCity());

                }
            }

            @Override
            public void onFailure(Call<String> call, Throwable t) {

            }
        });


    }

The instance is this passed to the service generator that uses scalar convertors, the body of the post request is saved as a simple string(as it was specified in the interface) and we can do with the response whatever we want.

sigillum_conf
  • 423
  • 3
  • 22