Android 如何在 Retrofit 请求的正文中发布原始的整个 JSON?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21398598/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 04:30:16  来源:igfitidea点击:

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

androidresthttp-postretrofitandroid-json

提问by user3243335

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?

这个问题以前可能有人问过,但没有得到明确的回答。如何在 Retrofit 请求的正文中发布原始的整个 JSON?

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.

在这里看到类似的问题。或者这个答案是否正确,它必须是表单 url 编码并作为字段传递?我真的希望不会,因为我连接的服务只是期待帖子正文中的原始 JSON。它们没有设置为查找 JSON 数据的特定字段。

I just want to clarify this with the restpertsonce 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.

我只是想与澄清这一restperts一劳永逸。一个人回答说不要使用 Retrofit。另一个不确定语法。另一个人认为是可以做到的,但前提是它的形式 url 编码并放置在一个字段中(这在我的情况下是不可接受的)。不,我无法为我的 Android 客户端重新编码所有服务。是的,在主要项目中发布原始 JSON 而不是将 JSON 内容作为字段属性值传递是很常见的。让我们做对了,继续前进。有人可以指出说明这是如何完成的文档或示例吗?或者提供一个可以/不应该这样做的正当理由。

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?

更新:我可以 100% 肯定地说一件事。您可以在 Google 的 Volley 中执行此操作。它是内置的。我们可以在 Retrofit 中做到这一点吗?

回答by Jake Wharton

The @Bodyannotation defines a single request body.

@Body注释定义了单个请求正文。

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

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

由于 Retrofit 默认使用 Gson,因此FooRequest实例将被序列化为 JSON 作为请求的唯一主体。

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 docshave much more on how object serialization works.

GSON文档有更多的关于对象序列化是如何工作的。

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:

现在,如果您真的真的想自己发送“原始”JSON 作为正文(但请为此使用 Gson!),您仍然可以使用TypedInput

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

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

TypedInput被定义为“具有相关 mime 类型的二进制数据。”。有两种方法可以使用上述声明轻松发送原始数据:

  1. Use TypedByteArrayto 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 TypedStringto create a TypedJsonStringclass:

    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.

  1. 使用TypedByteArray发送原始字节和 JSON mime 类型:

    String json = "{\"foo\":\"kit\",\"bar\":\"kat\"}";
    TypedInput in = new TypedByteArray("application/json", json.getBytes("UTF-8"));
    FooResponse response = foo.postRawJson(in);
    
  2. 子类TypedString以创建一个TypedJsonString类:

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

    然后使用类似于#1 的那个类的实例。

回答by learner

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

例如,我们也可以直接使用HashMap<String, Object>来发送主体参数,而不是类

interface Foo {
  @POST("/jayson")
  FooResponse postJson(@Body HashMap<String, Object> body);
}

回答by TommySM

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

是的,我知道现在已经晚了,但有人可能会从中受益。

Using Retrofit2:

使用改造2:

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.

昨晚我从 Volley 迁移到 Retrofit2 时遇到了这个问题(正如 OP 所说,这是直接内置到 Volley 中的JsonObjectRequest),虽然Jake 的答案是 Retrofit1.9 的正确答案,但 Retrofit2 没有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:

我的情况需要发送一个Map<String,Object>可能包含一些空值的 JSONObject(不会与 一起飞行@FieldMap,特殊字符也不会,有些会被转换),因此遵循@bnorms 提示,并如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.

可以使用 @Body 批注指定一个对象用作 HTTP 请求正文。

该对象也将使用在 Retrofit 实例上指定的转换器进行转换。如果不添加转换器,则只能使用RequestBody。

So this is an option using RequestBodyand ResponseBody:

所以这是一个使用RequestBodyand的选项ResponseBody

In your interface use @Bodywith RequestBody

在你的界面使用@BodyRequestBody

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:

在您的调用点中创建一个RequestBody,声明它是 MediaType,并使用 JSONObject 将您的 Map 转换为正确的格式:

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:

上面的优雅 Kotlin 版本,允许在应用程序代码的其余部分中从 JSON 转换中提取参数:

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

}

回答by Jonathan Nolasco Barrientos

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

Retrofit2 中,当您想以原始格式发送参数时,您必须使用Scalars

first add this in your gradle:

首先在你的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) {
    }
}

回答by superUser

Using JsonObjectis the way it is:

使用JsonObject方式是这样的:

  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());
        }
    };
    
  1. 像这样创建你的界面:

    public interface laInterfaz{ 
        @POST("/bleh/blah/org")
        void registerPayer(@Body JsonObject bean, Callback<JsonObject> callback);
    }
    
  2. 根据jsons结构制作JsonObject。

    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. 致电服务:

    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.

就是它!在我个人看来,这比制作 pojo 和处理课堂混乱要好得多。这样干净多了。

回答by zerobandwidth

I particularly like Jake's suggestion of the TypedStringsubclass 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.

我特别喜欢的Hyman的建议TypedString子类以上。您确实可以根据您计划推送的 POST 数据类型创建各种子类,每个子类都有自己的自定义一致调整集。

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

您还可以选择在 Retrofit API 中为 JSON POST 方法添加标头注释……

@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 ) ;

回答by Adil

1)Add dependencies-

1)添加依赖项-

 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

2)制作Api Handler类

    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

3)从 Json 模式 2 pojo 制作 bean 类

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

记住 -目标
语言:Java -源类型:JSON -注释样式:Gson -选择包括 getter 和 setter-你也可以选择允许附加属性

http://www.jsonschema2pojo.org/

http://www.jsonschema2pojo.org/

4)make interface fro api calling

4)为api调用制作接口

    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

5)制作JsonObject作为参数传入body

 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

6) 像这样调用 Api

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

回答by peresisUser

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

我发现当您使用复合对象作为@Body参数时,它无法与 RetrofitGSONConverter一起使用(假设您正在使用它)。您必须使用JsonObject而不是JSONObject在使用它时,它会添加NameValueParams而不详细说明它 - 如果您添加另一个日志拦截器依赖项和其他恶作剧,您只能看到这一点。

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

所以我发现解决这个问题的最佳方法是使用RequestBody. 您可以RequestBody通过一个简单的 api 调用将您的对象转换为并启动它。就我而言,我正在转换地图:

   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>

回答by ali-star

Add ScalarsConverterFactory to retrofit:

添加 ScalarsConverterFactory 进行改造:

in gradle:

在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"):

将您的调用接口 @Body 参数更改为 String,不要忘记添加@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.

现在您可以发布原始json。

回答by jatin rana

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

如果您不想为每个 API 调用创建 pojo 类,则可以使用 hashmap。

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