0

Background

On some API calls to the server, instead of the normal parameters to the interface, like this:

interface SomeInterface {
    @POST("first_launch") fun sendFirstLaunch(
        @Path("referral_code") referralCode: String?,
        @Path("referral_source") referralSource: String?): Call<BaseDCResponse>
}

We actually need to send those parameters as a JSON in the body.

The problem

I'm not an expert in Retrofit, but according to what I've found (here for example), I can only pass a Json String to the interface, meaning:

interface SomeInterface {
    @POST("first_launch") fun sendFirstLaunch(@Body jsonString: String): Call<BaseDCResponse>
}

According to here, I believe I can also send a serialized object, instead. Meaning something like:

interface SomeInterface {
    class SendFirstLaunchRequest(@Path("referral_code") val referralCode: String?,
        @Path("referral_source") val referralSource: String?)

    @POST("first_launch") fun sendFirstLaunch(
        @Body body: SendFirstLaunchRequest): Call<BaseDCResponse>
}

This loses the nice way to reach the function, while making me add the Json data manually for each function I put on the interface (or create new classes to pass there). I want to avoid this, and have something similar to the original.

What I've tried

I tried to search more and more about this, but it doesn't seem like this was requested.

Maybe I saw the answers but didn't understand them.

I think even the official website shows some clues about this:

https://square.github.io/retrofit/

Seeing that I don't think it's possible, I've also added a request for it here.

The questions

  1. Does Retrofit allow to send the parameters I set to the function, to be a Json data as a body?

  2. If not, is there any nice workaround for this? Am I correct that I could only pass a serialized object instead? If so, what's the proper way to do it? Maybe like here?

android developer
  • 114,585
  • 152
  • 739
  • 1,270
  • make a class for this and send into body like this data class Example( var name:String ) interface SomeInterface { @POST("first_launch") fun sendFirstLaunch(@Body jsonString: Example): Call } – Amit pandey Feb 18 '21 at 08:58
  • @Amitpandey That's what I wrote on #2. What about #1 ? Is it really impossible without creating more and more classes? – android developer Feb 18 '21 at 09:03

2 Answers2

0

Using Retrofit2:

I came across this problem last night migrating from Volley to Retrofit2 (and as OP states, this was built right into Volley with JsonObjectRequest), and although Jake's answer is the correct one for Retrofit1.9, Retrofit2 doesn't have TypedString.

My case required sending a Map<String,Object> that could contain some null values, converted to a JSONObject (that won't fly with @FieldMap, neither does special chars, some get converted), so following @bnorms hint, and as stated by Square:

An object can be specified for use as an HTTP request body with the @Body annotation.

The object will also be converted using a converter specified on the Retrofit instance. If no converter is added, only RequestBody can be used.

So this is an option using RequestBody and ResponseBody:

In your interface use @Body with RequestBody

  public interface ServiceApi
   {
      @POST("Your api end point")
      Call<ResponseBody> login(@Header("X_API_KEY") String header, @Body RequestBody body);  
   }

In your calling point create a RequestBody, stating it's MediaType, and using JSONObject to convert your Map to the proper format:

Map<String, Object> jsonParams = new ArrayMap<>();
//put something inside the map, could be null
jsonParams.put("name", some_code);

RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),(new JSONObject(jsonParams)).toString());
//serviceCaller is the interface initialized with retrofit.create...
Call<ResponseBody> response = serviceCaller.login(header, body);
      
response.enqueue(new Callback<ResponseBody>()
    {
        @Override
        public void onResponse(Call<ResponseBody> call,retrofit2.Response<ResponseBody> rawResponse)                                                
        {
            try
            {
             //get your response....
              Log.d(TAG, "RetroFit2.0 :RetroGetLogin: " + rawResponse.body().string());
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable throwable)
        {
        // other stuff...
        }
    });

Hope this Helps anyone!

Amit Joshi
  • 36
  • 4
0

Seems it doesn't, and that there is a request to offer something to help handling with this:

https://github.com/square/retrofit/issues/2890

android developer
  • 114,585
  • 152
  • 739
  • 1,270