这个问题以前可能被问过,但没有明确的答案。如何在Retrofit请求的主体中发布原始的整个JSON ?

在这里看到类似的问题。或者这个答案是正确的,它必须是表单url编码并作为一个字段传递?我真的希望不是,因为我要连接的服务只是希望在文章正文中有原始JSON。它们不是用来为JSON数据寻找特定字段的。

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.

更新:有一件事我可以百分之百确定。你可以在谷歌的Volley中做到这一点。这是与生俱来的。我们可以在Retrofit中这样做吗?


当前回答

我发现当你使用一个复合对象作为@Body参数时,它不能很好地与Retrofit的GSONConverter一起工作(假设你正在使用它)。 你必须使用JsonObject,而不是JsonObject,它添加了NameValueParams而没有详细说明它-你只能看到如果你添加了日志拦截器的另一个依赖项,和其他诡计。

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

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

这就是号召:

 @POST("openUsvDeal")
fun openUsvDeal(
        @Body params: RequestBody,
        @Query("timestamp") timeStamp: Long,
        @Query("appid") appid: String = Constants.APP_ID,
): Call<JsonObject>

其他回答

是的,我知道很晚了,但有人可能会从中受益。

使用Retrofit2:

我昨晚从Volley迁移到Retrofit2时遇到了这个问题(正如OP所述,这是用JsonObjectRequest构建到Volley中的),尽管Jake的答案是Retrofit1.9的正确答案,但Retrofit2没有TypedString。

我的情况下需要发送一个地图<字符串,对象>,可以包含一些空值,转换为JSONObject(不会飞@FieldMap,也不特殊字符,一些得到转换),所以遵循@ b规范提示,并由Square声明:

可以使用@Body注释指定对象作为HTTP请求主体。 对象还将使用Retrofit实例上指定的转换器进行转换。如果没有添加转换器,则只能使用RequestBody。

这是一个使用RequestBody和ResponseBody的选项:

在你的接口中使用@Body和RequestBody

public interface ServiceApi
{
    @POST("prefix/user/{login}")
    Call<ResponseBody> login(@Path("login") String postfix, @Body RequestBody params);  
}

在你的调用点创建一个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...
        }
    });

上面的一个优雅的Kotlin版本,允许在应用程序的其余代码中从JSON转换中抽象参数:

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

使用JsonObject是它的方式:

Create your interface like this: public interface laInterfaz{ @POST("/bleh/blah/org") void registerPayer(@Body JsonObject bean, Callback<JsonObject> callback); } 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*/ 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()); } };

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

解决了我的问题基于TommySM的答案(见前)。 但是我不需要登录,我使用Retrofit2来测试https GraphQL API,就像这样:

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; } } Defined the call procedure in the interface: @POST("/api/apiname") Call<BaseResponse> apicall(@Body RequestBody params); 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();

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

添加ScalarsConverterFactory进行改造:

在gradle:

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

你的改造:

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

改变你的调用接口@Body参数为String,不要忘记添加@Headers("Content-Type: application/json"):

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

现在你可以发布raw json了。