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
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:
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);
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.
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)
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);
}
JSONObject showing error please use
JsonObject paramObject = new JsonObject(); paramObject.addProperty("loginId", vMobile_Email);
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...
}
});
Hope this Helps anyone!
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 {
fun login(username: String, password: String) =
jsonLogin(createJsonRequestBody(
"username" to username, "password" to password))
@POST("/api/login")
fun jsonLogin(@Body params: RequestBody): Deferred<LoginResult>
private fun createJsonRequestBody(vararg params: Pair<String, String>) =
RequestBody.create(
okhttp3.MediaType.parse("application/json; charset=utf-8"),
JSONObject(mapOf(*params)).toString())
}
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>