How to POST raw whole JSON in the body of a Retrofit request?
Asked Answered
S

28

356

This question may have been asked before but no it was not definitively answered. How exactly does one post raw whole JSON inside the body of a Retrofit request?

See similar question here. Or is this answer correct that it must be form url encoded and passed as a field? I really hope not, as the services I am connecting to are just expecting raw JSON in the body of the post. They are not set up to look for a particular field for the JSON data.

I just want to clarify this with the restperts once and for all. One person answered not to use Retrofit. The other was not certain of the syntax. Another thinks yes it can be done but only if its form url-encoded and placed in a field (that's not acceptable in my case). No, I can't re-code all the services for my Android client. And yes, it's very common in major projects to post raw JSON instead of passing over JSON content as field property values. Let's get it right and move on. Can someone point to the documentation or example that shows how this is done? Or provide a valid reason why it can/should not be done.

UPDATE: One thing I can say with 100% certainty. You CAN do this in Google's Volley. It's built right in. Can we do this in Retrofit?

Successive answered 28/1, 2014 at 6:40 Comment(3)
The post of Jake Wharton is correct! Mark as answer!Taster
You might use jsonObject better.Obadias
works perfectly with RequestBody like this -> RequestBody body = RequestBody.create(MediaType.parse("text/plain"), text); for detailed answer futurestud.io/tutorials/…Bennie
S
523

The @Body annotation defines a single request body.

interface Foo {
  @POST("/jayson")
  FooResponse postJson(@Body FooRequest body);
}

Since Retrofit uses Gson by default, the FooRequest instances will be serialized as JSON as the sole body of the request.

public class FooRequest {
  final String foo;
  final String bar;

  FooRequest(String foo, String bar) {
    this.foo = foo;
    this.bar = bar;
  }
}

Calling with:

FooResponse = foo.postJson(new FooRequest("kit", "kat"));

Will yield the following body:

{"foo":"kit","bar":"kat"}

The Gson docs have much more on how object serialization works.

Now, if you really really want to send "raw" JSON as the body yourself (but please use Gson for this!) you still can using TypedInput:

interface Foo {
  @POST("/jayson")
  FooResponse postRawJson(@Body TypedInput body);
}

TypedInput is a defined as "Binary data with an associated mime type.". There's two ways to easily send raw data with the above declaration:

  1. Use TypedByteArray to send raw bytes and the JSON mime type:

    String json = "{\"foo\":\"kit\",\"bar\":\"kat\"}";
    TypedInput in = new TypedByteArray("application/json", json.getBytes("UTF-8"));
    FooResponse response = foo.postRawJson(in);
    
  2. Subclass TypedString to create a TypedJsonString class:

    public class TypedJsonString extends TypedString {
      public TypedJsonString(String body) {
        super(body);
      }
    
      @Override public String mimeType() {
        return "application/json";
      }
    }
    

    And then use an instance of that class similar to #1.

Sirenasirenic answered 29/1, 2014 at 5:47 Comment(10)
Very well, however, is there anyway to make this without making the pojos?Obadias
@Jake Wharton: how about using "JsonObject" from Gson? Does that work in a proper way?Buffybuford
@Jake Wharton TypedInput has been removed in the new versions? How do we do a raw json post?Encrimson
This is not working on retrofit 2. TypedInput and TypedString classes were removed.Johannejohannes
My problem is that the JSON content depends on the Object content. Let's say some fields on the JSON are optional, you include them depending on the field value. I don't know how to achieve this in Retrofit 2.Bordelon
@jakewharton What can we do for TypedString since it has been removed?Pistil
For Retrofit2, you can use RequestBody to create a raw body.Copious
I am getting this exception java.lang.IllegalArgumentException: Unable to create @Body converter for class MatchAPIRequestBody (parameter #1)Cultural
What should you annotate foo and bar with, to handle different name on the server? Also, is there a way to avoid the class, and have it all in the function postJson instead? Meaning that you could put the parameters as strings there directly, but just have it as json? I asked it here: https://mcmap.net/q/93911/-does-retrofit-support-passing-json-as-parameter-yet-call-it-as-normal-parameters/878126Quash
Don't forget to add @Serialized("foo")final String foo; @Serialized("bar") final String bar; otherwise you may get 400 errorPorky
K
194

Yes I know it's late, but somebody would probably benefit from this.

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("prefix/user/{login}")
    Call<ResponseBody> login(@Path("login") String postfix, @Body RequestBody params);  
}

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("code", 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("loginpostfix", 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...
        }
    });

An elegant Kotlin version of the above, to allow abstracting the parameters from the JSON convertion in the rest of your application code:

interface ServiceApi {

    @POST("/api/login")
    fun jsonLogin(@Body params: RequestBody): Deferred<LoginResult>

}

class ServiceApiUsingClass {

//ServiceApi init

    fun login(username: String, password: String) =
            serviceApi.jsonLogin(createJsonRequestBody(
                "username" to username, "password" to password))

    private fun createJsonRequestBody(vararg params: Pair<String, String>) =
            RequestBody.create(
                okhttp3.MediaType.parse("application/json; charset=utf-8"), 
                JSONObject(mapOf(*params)).toString())
}
Kumkumagai answered 24/4, 2016 at 9:7 Comment(7)
Yeah I'm seeing a lot of complicated responses all over for this. If you're using Retrofit2 and want to do volley's JsonObjectRequest, all you need to do is this. Good answer.Ahmedahmedabad
Retrofit addes a key named "nameValuePairs" to the top of all the json objects. How can i remove this @KumkumagaiMccarron
Thank you! This solved my problem. Now I can send JSONObject directly without creating POJOs.Ting
This is the only solution that helped me to post a null value to a property in the requestBody which otherwise was getting ignored.Sligo
I know i'm late but what is jsonParams.put("code", some_code); in the third line ?Clarino
@NaveenNiraula you are correct, it is the map object two lines above - I edited my answer, thank you :) it is an example of how to add params to the JSON you are sendingKumkumagai
I wasn't able to get this Kotlin code working either, ran into the same problem free_style did.Hardin
P
182

Instead of classes we can also directly use the HashMap<String, Object> to send body parameters for example

interface Foo {
  @POST("/jayson")
  FooResponse postJson(@Body HashMap<String, Object> body);
}
Pilewort answered 25/2, 2015 at 6:4 Comment(10)
What about if the json you we trying to send is kinda complex? like having arrays, and objects?Obadias
At that time you can create Hash map like HashMap<String,Object> ,it can be possible for creating kinda complex arrays and objects JSON.Pilewort
This is excellent if you do not want to be tied to a POJO of some kind.Mesothorium
Retrofit addes a key named "nameValuePairs" to the top of all the json objects. How can i remove this @BoopathiMccarron
@Nil you cannot send json object by using retrofit...you adhere with pojo or my answer...this is nature of retrofit.if you want more about this ask Jake Wharton he is retrofit developer guy, his answer also available with pojo.Pilewort
@Nil By using Typed input you can achieve , there is conversion between json Typed input.Look about that.Pilewort
I get IllegalArgumentException: Unable to create @Body converter for java.util.HashMap<java.lang.String, java.lang.Object> with Moshi. I think Gson is needed for this to workReliance
Seems like using Map<String, Object> solved the issue with MoshiReliance
If using Kotlin use a hashmap of <String, Any>Stamina
Just val requestBody = RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"), YOUR_JSON_OBJECT.toString())Carbon
H
103

In Retrofit2, When you want to send your parameters in raw you must use Scalars.

first add this in your gradle:

compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.squareup.retrofit2:converter-scalars:2.3.0'

Your Interface

public interface ApiInterface {

    String URL_BASE = "http://10.157.102.22/rest/";

    @Headers("Content-Type: application/json")
    @POST("login")
    Call<User> getUser(@Body String body);

}

Activity

   public class SampleActivity extends AppCompatActivity implements Callback<User> {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sample);

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(ApiInterface.URL_BASE)
                .addConverterFactory(ScalarsConverterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();

        ApiInterface apiInterface = retrofit.create(ApiInterface.class);


        // prepare call in Retrofit 2.0
        try {
            JSONObject paramObject = new JSONObject();
            paramObject.put("email", "[email protected]");
            paramObject.put("pass", "4384984938943");

            Call<User> userCall = apiInterface.getUser(paramObject.toString());
            userCall.enqueue(this);
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }


    @Override
    public void onResponse(Call<User> call, Response<User> response) {
    }

    @Override
    public void onFailure(Call<User> call, Throwable t) {
    }
}
Hanhana answered 22/5, 2017 at 1:54 Comment(8)
The trick here is Scalar adapter before Gson, otherwise Gson will wrap your manually serialized JSON in a String.Leannaleanne
jonathan-nolasco-barrientos you have to change .baseUrl(ApiInterface.ENDPOINT) to .baseUrl(ApiInterface.URL_BASE )Elliotelliott
When you use GsonConverterFactory, the .toString() is not necessary. You can declare Call<User> getUser(@Body JsonObject body); using JsonObject instead of JSONObject and pass the paramObject directly. It will work just fine.Aldus
This also works, but doesn't offer the freedom of dealing with your body in case it is not a StringTye
This is the only solution that allows you to use JSON such as [{"var1":"x","var2":"y"}]Eastereasterday
@IgordeLorenzi solve my issue, since I'm using spring boot to retrieve the json only JsonObject from gson works fineXerarch
@IgordeLorenzi Is there a difference which one is better JSONObject or JsonObject to use with scalars.Gatling
@SumitShukla, as far as I know both must support the use of scalars well. If you want to see more opinions about some other differences: #29043122Aldus
O
51

Using JsonObject is the way it is:

  1. Create your interface like this:

    public interface laInterfaz{ 
        @POST("/bleh/blah/org")
        void registerPayer(@Body JsonObject bean, Callback<JsonObject> callback);
    }
    
  2. Make the JsonObject acording to the jsons structure.

    JsonObject obj = new JsonObject();
    JsonObject payerReg = new JsonObject();
    payerReg.addProperty("crc","aas22");
    payerReg.addProperty("payerDevManufacturer","Samsung");
    obj.add("payerReg",payerReg);
    /*json/*
        {"payerReg":{"crc":"aas22","payerDevManufacturer":"Samsung"}}
    /*json*/
    
  3. Call the service:

    service.registerPayer(obj, callBackRegistraPagador);
    
    Callback<JsonObject> callBackRegistraPagador = new Callback<JsonObject>(){
        public void success(JsonObject object, Response response){
            System.out.println(object.toString());
        }
    
        public void failure(RetrofitError retrofitError){
            System.out.println(retrofitError.toString());
        }
    };
    

And that its! In my personal opinion, its a lot better than making pojos and working with the class mess. This is a lot more cleaner.

Obadias answered 27/7, 2015 at 16:8 Comment(6)
What if i dont want to send specif value in jsonobject class. which annotaion can i use above veriable for that?Antidromic
As you can see the above example... JsonObject as it is an object, does not use any anotation. In your case if you dont want to send specific value, you might just not add it as a property...Obadias
I mean i dont want to send value which is declared in the class. Btw i fixed the problem. There was a annotation for that which name is expose.Antidromic
This is the most flexible way. You can construct your json object even if you don't know how many fields you will have or even if you don't know they names +1 from meFranciskus
i m getting error Service methods cannot return void. for method APIServices.signUpUserLim
This is sure to work guys and also, you don't need to create all those model classes for requests and reponses. meaning more flexibilityFiring
O
15

Add ScalarsConverterFactory to retrofit:

in gradle:

implementation'com.squareup.retrofit2:converter-scalars:2.5.0'

your retrofit:

retrofit = new Retrofit.Builder()
            .baseUrl(WEB_DOMAIN_MAIN)
            .addConverterFactory(ScalarsConverterFactory.create())
            .addConverterFactory(GsonConverterFactory.create(gson))
            .build();

change your call interface @Body parameter to String, don't forget to add @Headers("Content-Type: application/json"):

@Headers("Content-Type: application/json")
@POST("/api/getUsers")
Call<List<Users>> getUsers(@Body String rawJsonString);

now you can post raw json.

Oscilloscope answered 24/1, 2019 at 5:18 Comment(2)
it works !!you save my day! i am also using moshi with ScalarsConverterFactory. No issue so far.Columnar
It works good, thank youBellarmine
S
12

I particularly like Jake's suggestion of the TypedString subclass above. You could indeed create a variety of subclasses based on the sorts of POST data you plan to push up, each with its own custom set of consistent tweaks.

You also have the option of adding a header annotation to your JSON POST methods in your Retrofit API…

@Headers( "Content-Type: application/json" )
@POST("/json/foo/bar/")
Response fubar( @Body TypedString sJsonBody ) ;

…but using a subclass is more obviously self-documenting.

@POST("/json/foo/bar")
Response fubar( @Body TypedJsonString jsonBody ) ;
Seitz answered 11/3, 2015 at 13:48 Comment(1)
Saved the day with a clear example using TypedJsonString from JW suggestionEllieellinger
C
11

1)Add dependencies-

 compile 'com.google.code.gson:gson:2.6.2'
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'

2) make Api Handler class

    public class ApiHandler {


  public static final String BASE_URL = "URL";  

    private static Webservices apiService;

    public static Webservices getApiService() {

        if (apiService == null) {

           Gson gson = new GsonBuilder()
                    .setLenient()
                    .create();
            Retrofit retrofit = new Retrofit.Builder().addConverterFactory(GsonConverterFactory.create(gson)).baseUrl(BASE_URL).build();

            apiService = retrofit.create(Webservices.class);
            return apiService;
        } else {
            return apiService;
        }
    }


}

3)make bean classes from Json schema 2 pojo

Remember
-Target language : Java -Source type : JSON -Annotation style : Gson -select Include getters and setters -also you may select Allow additional properties

http://www.jsonschema2pojo.org/

4)make interface fro api calling

    public interface Webservices {

@POST("ApiUrlpath")
    Call<ResponseBean> ApiName(@Body JsonObject jsonBody);

}

if you have a form-data parameters then add below line

@Headers("Content-Type: application/x-www-form-urlencoded")

Other way for form-data parameter check this link

5)make JsonObject for passing in to body as parameter

 private JsonObject ApiJsonMap() {

    JsonObject gsonObject = new JsonObject();
    try {
        JSONObject jsonObj_ = new JSONObject();
        jsonObj_.put("key", "value");
        jsonObj_.put("key", "value");
        jsonObj_.put("key", "value");


        JsonParser jsonParser = new JsonParser();
        gsonObject = (JsonObject) jsonParser.parse(jsonObj_.toString());

        //print parameter
        Log.e("MY gson.JSON:  ", "AS PARAMETER  " + gsonObject);

    } catch (JSONException e) {
        e.printStackTrace();
    }

    return gsonObject;
}

6) Call Api Like this

private void ApiCallMethod() {
    try {
        if (CommonUtils.isConnectingToInternet(MyActivity.this)) {
            final ProgressDialog dialog;
            dialog = new ProgressDialog(MyActivity.this);
            dialog.setMessage("Loading...");
            dialog.setCanceledOnTouchOutside(false);
            dialog.show();

            Call<ResponseBean> registerCall = ApiHandler.getApiService().ApiName(ApiJsonMap());
            registerCall.enqueue(new retrofit2.Callback<ResponseBean>() {
                @Override
                public void onResponse(Call<ResponseBean> registerCall, retrofit2.Response<ResponseBean> response) {

                    try {
                        //print respone
                        Log.e(" Full json gson => ", new Gson().toJson(response));
                        JSONObject jsonObj = new JSONObject(new Gson().toJson(response).toString());
                        Log.e(" responce => ", jsonObj.getJSONObject("body").toString());

                        if (response.isSuccessful()) {

                            dialog.dismiss();
                            int success = response.body().getSuccess();
                            if (success == 1) {



                            } else if (success == 0) {



                            }  
                        } else {
                            dialog.dismiss();


                        }


                    } catch (Exception e) {
                        e.printStackTrace();
                        try {
                            Log.e("Tag", "error=" + e.toString());

                            dialog.dismiss();
                        } catch (Resources.NotFoundException e1) {
                            e1.printStackTrace();
                        }

                    }
                }

                @Override
                public void onFailure(Call<ResponseBean> call, Throwable t) {
                    try {
                        Log.e("Tag", "error" + t.toString());

                        dialog.dismiss();
                    } catch (Resources.NotFoundException e) {
                        e.printStackTrace();
                    }
                }

            });

        } else {
            Log.e("Tag", "error= Alert no internet");


        }
    } catch (Resources.NotFoundException e) {
        e.printStackTrace();
    }
}
Campanula answered 17/1, 2018 at 11:47 Comment(0)
S
10

I found that when you use a compound object as @Body params, it could not work well with the Retrofit's GSONConverter (under the assumption you are using that). You have to use JsonObject and not JSONObject when working with that, it adds NameValueParams without being verbose about it - you can only see that if you add another dependency of logging interceptor, and other shenanigans.

So what I found the best approach to tackle this is using RequestBody. You turn your object to RequestBody with a simple api call and launch it. In my case I'm converting a map:

   val map = HashMap<String, Any>()
        map["orderType"] = orderType
        map["optionType"] = optionType
        map["baseAmount"] = baseAmount.toString()
        map["openSpotRate"] = openSpotRate.toString()
        map["premiumAmount"] = premiumAmount.toString()
        map["premiumAmountAbc"] = premiumAmountAbc.toString()
        map["conversionSpotRate"] = (premiumAmountAbc / premiumAmount).toString()
        return RequestBody.create(MediaType.parse("application/json; charset=utf-8"), JSONObject(map).toString())

and this is the call:

 @POST("openUsvDeal")
fun openUsvDeal(
        @Body params: RequestBody,
        @Query("timestamp") timeStamp: Long,
        @Query("appid") appid: String = Constants.APP_ID,
): Call<JsonObject>
Stamina answered 28/1, 2019 at 8:47 Comment(1)
Well this helped me after googling overnight.Unburden
Z
10

This is what works me for the current version of retrofit 2.6.2,

First of all, we need to add a Scalars Converter to the list of our Gradle dependencies, which would take care of converting java.lang.String objects to text/plain request bodies,

implementation'com.squareup.retrofit2:converter-scalars:2.6.2'

Then, we need to pass a converter factory to our Retrofit builder. It will later tell Retrofit how to convert the @Body parameter passed to the service.

private val retrofitBuilder: Retrofit.Builder by lazy {
    Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(ScalarsConverterFactory.create())
        .addConverterFactory(GsonConverterFactory.create())
}

Note: In my retrofit builder i have two converters Gson and Scalars you can use both of them but to send Json body we need to focus Scalars so if you don't need Gson remove it

Then Retrofit service with a String body parameter.

@Headers("Content-Type: application/json")
@POST("users")
fun saveUser(@Body   user: String): Response<MyResponse>

Then create the JSON body

val user = JsonObject()
 user.addProperty("id", 001)
 user.addProperty("name", "Name")

Call your service

RetrofitService.myApi.saveUser(user.toString())
Zilber answered 21/11, 2019 at 7:22 Comment(0)
M
7

You can use hashmap if you don't want to create pojo class for every API call.

HashMap<String,String> hashMap=new HashMap<>();
        hashMap.put("email","[email protected]");
        hashMap.put("password","1234");

And then send like this

Call<JsonElement> register(@Body HashMap registerApiPayload);
Mingrelian answered 5/3, 2019 at 11:3 Comment(0)
T
5

After so much effort, found that the basic difference is you need to send the JsonObject instead of JSONObject as parameter.

Thain answered 5/3, 2018 at 18:3 Comment(2)
I was also doing same mistake :pMistakable
Thank you so much for your answer. I was doing same mistake.Basham
B
5

use following to send json

final JSONObject jsonBody = new JSONObject();
    try {

        jsonBody.put("key", "value");

    } catch (JSONException e){
        e.printStackTrace();
    }
    RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),(jsonBody).toString());

and pass it to url

@Body RequestBody key
Bultman answered 2/8, 2018 at 19:27 Comment(0)
C
5

If you don't want to create extra classes or use JSONObject you can use a HashMap.

Retrofit interface:

@POST("/rest/registration/register")
fun signUp(@Body params: HashMap<String, String>): Call<ResponseBody>

Call:

val map = hashMapOf(
    "username" to username,
    "password" to password,
    "firstName" to firstName,
    "surname" to lastName
)

retrofit.create(TheApi::class.java)
     .signUp(map)
     .enqueue(callback)
Cottrell answered 22/1, 2019 at 20:2 Comment(0)
R
5

For more clarity on the answers given here, this is how you can use the extension functions. This is only if you are using Kotlin

If you are using com.squareup.okhttp3:okhttp:4.0.1 the older methods of creating objects of MediaType and RequestBody have been deprecated and cannot be used in Kotlin.

If you want to use the extension functions to get a MediaType object and a ResponseBody object from your strings, firstly add the following lines to the class in which you expect to use them.

import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody

You can now directly get an object of MediaType this way

val mediaType = "application/json; charset=utf-8".toMediaType()

To get an object of RequestBody first convert the JSONObject you want to send to a string this way. You have to pass the mediaType object to it.

val requestBody = myJSONObject.toString().toRequestBody(mediaType)
Regen answered 8/8, 2019 at 12:41 Comment(0)
M
5

Things required to send raw json in Retrofit.

1) Make sure to add the following header and remove any other duplicate header. Since, on Retrofit's official documentation they specifically mention-

Note that headers do not overwrite each other. All headers with the same name will be included in the request.

@Headers({"Content-Type: application/json"})

2) a. If you are using a converter factory you can pass your json as a String, JSONObject, JsonObject and even a POJO. Also have checked, having ScalarConverterFactory is not necessary only GsonConverterFactory does the job.

@POST("/urlPath")
@FormUrlEncoded
Call<Response> myApi(@Header("Authorization") String auth, @Header("KEY") String key, 
                     @Body JsonObject/POJO/String requestBody);

2) b. If you are NOT using any converter factory then you MUST use okhttp3's RequestBody as Retrofit's documentation says-

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

RequestBody requestBody=RequestBody.create(MediaType.parse("application/json; charset=utf-8"),jsonString);

@POST("/urlPath")
@FormUrlEncoded
Call<Response> myApi(@Header("Authorization") String auth, @Header("KEY") String key, 
                 @Body RequestBody requestBody);

3) Success!!

Manet answered 6/2, 2020 at 12:58 Comment(2)
Expected a string but was BEGIN_OBJECT at line 1 column 2 path $Glycol
@AhamadullahSaikat Your json string which gson is trying to parse doesn't have the same structure as that of the pojo provided to it.Manet
R
4

Based on the top answer, I have a solution to not have to make POJOs for every request.

Example, I want to post this JSON.

{
    "data" : {
        "mobile" : "qwer",
        "password" : "qwer"
    },
    "commom" : {}
}

then, I create a common class like this:

import java.util.Map;
import java.util.HashMap;

public class WRequest {

    Map<String, Object> data;
    Map<String, Object> common;

    public WRequest() {
        data = new HashMap<>();
        common = new HashMap<>();
    }
}

Finally, when I need a json

WRequest request = new WRequest();
request.data.put("type", type);
request.data.put("page", page);

The request marked annotation @Body then can pass to Retrofit.

Racket answered 10/8, 2018 at 13:1 Comment(0)
R
3

API Call

@Headers("Content-Type: application/json")
@POST("/set_data")
Call<CommonResponse> setPreferences(@Body RequestData request);

Note: Use GSON library of Retrofit

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class RequestData {

    @SerializedName("access_token")
    @Expose
    private String accessToken;

    @SerializedName("data")
    @Expose
    private Data data;
    // The above 'Data' is another similar class to add inner JSON objects. JSONObject within a JSONObject.

    public void setAccessToken(String accessToken) {
        this.accessToken = accessToken;
    }

    public void setData(Data data) {
        this.data = data;
    }
}

I guess that will help, rest all integration you might already have had and we don't need anything fancy to use above code snippet. It's working perfectly for me.

Ruffin answered 30/6, 2020 at 11:29 Comment(0)
P
3

you need to set @Body in interface

@Headers({ "Content-Type: application/json;charset=UTF-8"})
    @POST("Auth/Login")
    Call<ApiResponse> loginWithPhone(@Body HashMap<String, String> fields);

To pass the raw body to retrofit just use:

 HashMap<String,String> SendData =new HashMap<>();
        SendData.put("countryCode",ccode);
        SendData.put("phoneNumber",phone);

        Call<ApiResponse>call = serviceInterface.loginWithPhone(SendData);

this works for me:

Peipeiffer answered 25/11, 2020 at 3:26 Comment(0)
O
2

Solved my problem based on TommySM answer (see previous). But I didn't need to make login, I used Retrofit2 for testing https GraphQL API like this:

  1. Defined my BaseResponse class with the help of json annotations (import jackson.annotation.JsonProperty).

    public class MyRequest {
        @JsonProperty("query")
        private String query;
    
        @JsonProperty("operationName")
        private String operationName;
    
        @JsonProperty("variables")
        private String variables;
    
        public void setQuery(String query) {
            this.query = query;
        }
    
        public void setOperationName(String operationName) {
            this.operationName = operationName;
        }
    
        public void setVariables(String variables) {
            this.variables = variables;
        }
    }
    
  2. Defined the call procedure in the interface:

    @POST("/api/apiname")
    Call<BaseResponse> apicall(@Body RequestBody params);
    
  3. Called apicall in the body of test: Create a variable of MyRequest type (for example "myLittleRequest").

    Map<String, Object> jsonParams = convertObjectToMap(myLittleRequest);
    RequestBody body = 
         RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),
                        (new JSONObject(jsonParams)).toString());
    response = hereIsYourInterfaceName().apicall(body).execute();
    
Ornithomancy answered 9/10, 2019 at 12:39 Comment(0)
L
2

I wanted to compare speed of volley and retrofit for sending and receiving data I wrote below code (for retrofit part)

first dependency:

dependencies {
     implementation 'com.squareup.retrofit2:retrofit:2.4.0'
     implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
}

Then interface:

 public interface IHttpRequest {

    String BaseUrl="https://example.com/api/";

    @POST("NewContract")
    Call<JsonElement> register(@Body HashMap registerApiPayload);
}

and a function to set parameters to post data to server(In MainActivity):

private void Retrofit(){

    Retrofit retrofitRequest = new Retrofit.Builder()
            .baseUrl(IHttpRequest.BaseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    // set data to send
    HashMap<String,String> SendData =new HashMap<>();
    SendData.put("token","XYXIUNJHJHJHGJHGJHGRTYTRY");
    SendData.put("contract_type","0");
    SendData.put("StopLess","37000");
    SendData.put("StopProfit","48000");

    final IHttpRequest request=retrofitRequest.create(IHttpRequest.class);

    request.register(SendData).enqueue(new Callback<JsonElement>() {
        @Override
        public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {
            if (response.isSuccessful()){
                Toast.makeText(getApplicationContext(),response.body().toString(),Toast.LENGTH_LONG).show();
            }
        }

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

        }
    });

}

And I found Retrofit faster than volley in my case.

Levorotatory answered 6/12, 2019 at 14:37 Comment(0)
R
2

Updated solution for 2022:

One of the first things to check is that your post request is working via a third party API such as postman. I had done this before coming across the solutions on this page.

The next step is to add logging facilities to your retrofit instance. Click here on how to add logging to retrofit.

Upon adding logging I saw a 500 server error, based on the fact that the end-point was working via Postman we know that the error must be something to do with the format of the data that is passed to the Post method.

Your retrofit builder should look like this:

val retrofitInstance = Retrofit.Builder()
            .baseUrl("https://pacific-tundra-61285.herokuapp.com/")
            .addConverterFactory(ScalarsConverterFactory.create())
            .addConverterFactory(GsonConverterFactory.create())
            .client(httpClient)
            .build()

This post helped a lot in helping solve this problem and provided the correct way to convert the object into the correct "application/json" format when making the post request. There were a few deprecated methods used in the kotlin version, the new code is very similar:

private fun createRequestBody(vararg params : Pair<String, Any>) =
        JSONObject(mapOf(*params)).toString()
            .toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())

The generic value parameter in the pair is set to Any so that you can handle the different types related to your object.

The final piece just for clarity is the actual post method and the code that is used to invoke the post request.

@POST("create/")
    fun create(@Body params : RequestBody) : Call<YourObject>
val call = apiService.create(createRequestBody( 
"string" to object // You should pass in any key and value pairs here.

Finally call enqueue on the call as usual.

Roumell answered 28/3, 2022 at 13:33 Comment(0)
E
1

I tried this: When you are creating your Retrofit instance, add this converter factory to the retrofit builder:

gsonBuilder = new GsonBuilder().serializeNulls()     
your_retrofit_instance = Retrofit.Builder().addConverterFactory( GsonConverterFactory.create( gsonBuilder.create() ) )
Embower answered 18/10, 2018 at 13:32 Comment(0)
W
1

While creating OkHttpClient that will be used for Retrofit.

add an Interceptor like this.

 private val httpClient = OkHttpClient.Builder()
        .addInterceptor (other interceptors)
        ........................................

        //This Interceptor is the main logging Interceptor
        .addInterceptor { chain ->
            val request = chain.request()
            val jsonObj = JSONObject(Gson().toJson(request))

            val requestBody = (jsonObj
            ?.getJSONObject("tags")
            ?.getJSONObject("class retrofit2.Invocation")
            ?.getJSONArray("arguments")?.get(0) ?: "").toString()
            val url = jsonObj?.getJSONObject("url")?.getString("url") ?: ""
            
            Timber.d("gsonrequest request url: $url")
            Timber.d("gsonrequest body :$requestBody")

            chain.proceed(request)
        }
        
        ..............
        // Add other configurations
        .build()

Now your every Retrofit call's URL and request body will be logged in Logcat. Filter it by "gsonrequest"

Webbed answered 15/5, 2020 at 15:14 Comment(0)
J
1

enter image description here

Add ScalarsConverterFactory.create() method and pass hard code

Jotunheim answered 18/12, 2020 at 9:17 Comment(0)
A
0

JSONObject showing error please use

JsonObject paramObject = new JsonObject(); paramObject.addProperty("loginId", vMobile_Email);

Application answered 19/11, 2020 at 13:27 Comment(1)
Hi, when you use a code example try to use the correct format, it's more clear to see.Enter
P
0
@Headers(value = "Content-Type: application/json")
@POST("api/Persona/Add")
Call<Persona> AddPersona(@Header("authorization") String token, @Body JsonObject object);
 JsonObject postParam = new JsonObject();
       postParam.addProperty("PersonaCedula", item.getPersonaCedula());
Plaintiff answered 19/5, 2022 at 17:38 Comment(0)
G
0

ApiService.kt Class

@POST("profile")
fun get_profile( @Body user_registering: RequestBody?, @Query("type") type: String?): Call<ProfileResponse?>?

Call below the fun for POST raw whole JSON in the body.

private fun updateProfile(name: String, username: String,email: String,phone: String,   url:String? ) {
    if (AppUtil.isInternetAvailable(requireContext())) {
        binding.loader.visibility = View.VISIBLE
        val jsonObject = JSONObject()
        try {
            jsonObject.put("name", name)
            jsonObject.put("username", username)
            jsonObject.put("email", email)
            jsonObject.put("phone", phone)
            jsonObject.put(
                "token",
                Prefs.getPrefInstance()!!.getValue(requireContext(), Const.TOKEN, "")
            )
            //                jsonObject.put("image", uploaded_image);
            jsonObject.put("image", url)
        } catch (e: JSONException) {
            binding.loader.visibility = View.GONE
            e.printStackTrace()
        }
        val params = jsonObject.toString()
        val user_registering = params.toRequestBody("application/json".toMediaTypeOrNull())
        APIUtils.getAPIService()?.get_profile(user_registering, "ProfileUpdate")
            ?.enqueue(object : Callback<ProfileResponse?> {
                override fun onResponse(
                    call: Call<ProfileResponse?>,
                    response: Response<ProfileResponse?>
                ) {
                    if (response.body() != null) {
                        if (response.body()!!.getStatus() != null && response.body()!!.getStatus()!!
                                .equals(200)
                        ) {
                            binding.loader.visibility = View.GONE
                            val dialog_view = LayoutInflater.from(context).inflate(
                                AppUtil.setLanguage(
                                    context!!, R.layout.simple_dialog_text_button
                                ), null
                            )
                            val dialog = AlertDialog.Builder(
                                context!!
                            )
                                .setCancelable(false)
                                .setView(dialog_view)
                                .show()
                            if (dialog.window != null) dialog.window!!.decorView.background.alpha =
                                0
                            (dialog_view.findViewById<View>(R.id.dialog_text) as TextView).text =
                                Html.fromHtml(response.body()!!.getMessage())
                            dialog_view.findViewById<View>(R.id.dialog_cancel).visibility =
                                View.GONE
                            dialog_view.findViewById<View>(R.id.dialog_ok)
                                .setOnClickListener { view: View? ->
                                    dialog.dismiss()
                                    activity!!.onBackPressed()
                                    binding.nameInput.clearFocus()
                                    binding.emailInput.clearFocus()
                                    binding.usernameInput.clearFocus()
                                    binding.phoneInput.clearFocus()
                                }
                        } else if (response.body()!!.getStatus() != null && response.body()!!.getStatus()!!
                                .equals(401)
                        ) {
                            binding.loader.visibility = View.GONE
                            val i = Intent(context, Login::class.java)
                            startActivity(i)
                            requireActivity().finish()
                        } else {
                            binding.loader.visibility = View.GONE
                            val dialog_view = LayoutInflater.from(context).inflate(
                                AppUtil.setLanguage(
                                    context!!, R.layout.simple_dialog_text_button
                                ), null
                            )
                            val dialog = AlertDialog.Builder(
                                context!!
                            )
                                .setCancelable(false)
                                .setView(dialog_view)
                                .show()
                            if (dialog.window != null) dialog.window!!.decorView.background.alpha =
                                0
                            (dialog_view.findViewById<View>(R.id.dialog_text) as TextView).text =
                                Html.fromHtml(response.body()!!.getMessage())
                            dialog_view.findViewById<View>(R.id.dialog_ok).visibility =
                                View.GONE
                            (dialog_view.findViewById<View>(R.id.dialog_cancel) as Button).text =
                                "OK"
                            dialog_view.findViewById<View>(R.id.dialog_cancel)
                                .setOnClickListener { view: View? -> dialog.dismiss() }
                        }
                    } else {
                        binding.loader.visibility = View.GONE
                        val dialog_view = LayoutInflater.from(context).inflate(
                            AppUtil.setLanguage(
                                context!!, R.layout.simple_dialog_text_button
                            ), null
                        )
                        val dialog = AlertDialog.Builder(
                            context!!
                        )
                            .setCancelable(false)
                            .setView(dialog_view)
                            .show()
                        if (dialog.window != null) dialog.window!!.decorView.background.alpha =
                            0
                        (dialog_view.findViewById<View>(R.id.dialog_text) as TextView).text =
                            "Something went wrong! Please try again."
                        dialog_view.findViewById<View>(R.id.dialog_ok).visibility =
                            View.GONE
                        (dialog_view.findViewById<View>(R.id.dialog_cancel) as Button).text =
                            "OK"
                        dialog_view.findViewById<View>(R.id.dialog_cancel)
                            .setOnClickListener { view: View? -> dialog.dismiss() }
                    }
                }

                override fun onFailure(call: Call<ProfileResponse?>, t: Throwable) {
                    t.printStackTrace()
                    binding.loader.visibility = View.GONE
                    val dialog_view = LayoutInflater.from(context).inflate(
                        AppUtil.setLanguage(
                            context!!, R.layout.simple_dialog_text_button
                        ), null
                    )
                    val dialog = AlertDialog.Builder(
                        context!!
                    )
                        .setCancelable(false)
                        .setView(dialog_view)
                        .show()
                    if (dialog.window != null) dialog.window!!.decorView.background.alpha = 0
                    (dialog_view.findViewById<View>(R.id.dialog_text) as TextView).text =
                        "Something went wrong! Please try again."
                    dialog_view.findViewById<View>(R.id.dialog_ok).visibility = View.GONE
                    (dialog_view.findViewById<View>(R.id.dialog_cancel) as Button).text =
                        "OK"
                    dialog_view.findViewById<View>(R.id.dialog_cancel)
                        .setOnClickListener { view: View? -> dialog.dismiss() }
                }
            })
    } else {
        val dialog_view = LayoutInflater.from(context).inflate(
            AppUtil.setLanguage(
                requireContext(), R.layout.simple_dialog_text_button
            ), null
        )
        val dialog = AlertDialog.Builder(
            requireContext()
        )
            .setCancelable(false)
            .setView(dialog_view)
            .show()
        if (dialog.window != null) dialog.window!!.decorView.background.alpha = 0
        (dialog_view.findViewById<View>(R.id.dialog_text) as TextView).text =
            resources.getString(R.string.no_internet_connection)
        dialog_view.findViewById<View>(R.id.dialog_ok).visibility = View.GONE
        (dialog_view.findViewById<View>(R.id.dialog_cancel) as Button).text =
            "OK"
        dialog_view.findViewById<View>(R.id.dialog_cancel)
            .setOnClickListener { view: View? -> dialog.dismiss() }
    }
}
Gabrielegabriell answered 25/9, 2023 at 2:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.