How to POST raw whole JSON in the body of a Retrof

2018-12-31 05:30发布

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?

11条回答
妖精总统
2楼-- · 2018-12-31 05:39

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.

查看更多
栀子花@的思念
3楼-- · 2018-12-31 05:41

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 ) ;
查看更多
临风纵饮
4楼-- · 2018-12-31 05:43

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
查看更多
与君花间醉酒
5楼-- · 2018-12-31 05:45

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())

}
查看更多
刘海飞了
6楼-- · 2018-12-31 05:56

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);
}
查看更多
唯独是你
7楼-- · 2018-12-31 05:56

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

查看更多
登录 后发表回答