如何在Retrofit请求的主体中发布原始整个JSON?

use*_*335 259 rest android http-post retrofit android-json

这个问题可能以前曾被问过,但没有得到明确答复.如何在Retrofit请求的主体内发布原始整个JSON?

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

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

更新:我可以100%确定地说一件事.你可以在谷歌的排球中做到这一点.它是内置的.我们可以在Retrofit中做到这一点吗?

Jak*_*ton 438

@Body注解定义单个请求体.

interface Foo {
  @POST("/jayson")
  FooResponse postJson(@Body FooRequest body);
}
Run Code Online (Sandbox Code Playgroud)

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

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

  FooRequest(String foo, String bar) {
    this.foo = foo;
    this.bar = bar;
  }
}
Run Code Online (Sandbox Code Playgroud)

致电:

FooResponse = foo.postJson(new FooRequest("kit", "kat"));
Run Code Online (Sandbox Code Playgroud)

将产生以下身体:

{"foo":"kit","bar":"kat"}
Run Code Online (Sandbox Code Playgroud)

GSON文档对对象序列化是如何工作的更多.

现在,如果你真的想要自己发送"原始"JSON作为身体(但请使用Gson!)你仍然可以使用TypedInput:

interface Foo {
  @POST("/jayson")
  FooResponse postRawJson(@Body TypedInput body);
}
Run Code Online (Sandbox Code Playgroud)

TypedInput定义为"具有关联mime类型的二进制数据".使用上述声明可以通过两种方式轻松发送原始数据:

  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);
    
    Run Code Online (Sandbox Code Playgroud)
  2. 子类TypedString创建一个TypedJsonString类:

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

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

  • 这不适用于改造2.已删除TypedInput和TypedString类. (22认同)
  • 对于Retrofit2,您可以使用`RequestBody`来创建原始主体. (10认同)
  • 然而,很好的是,无论如何在不制作pojos的情况下制作它? (4认同)
  • 我得到这个异常`java.lang.IllegalArgumentException:无法为类MatchAPIRequestBody创建@Body转换器(参数#1) (3认同)
  • @jakewharton我们可以为'TypedString`做什么,因为它已被删除? (2认同)

Boo*_*thi 148

我们也可以直接使用HashMap<String, Object>发送体参数来代替类

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

  • 我得到`IllegalArgumentException:无法使用Moshi为java.util.HashMap <java.lang.String,java.lang.Object>`创建@Body转换器.我认为需要Gson来实现这一目标 (5认同)
  • 如果使用 Kotlin,请使用 &lt;String, Any&gt; 的哈希图 (4认同)
  • 那时你可以创建像HashMap <String,Object>这样的Hash映射,它可以创建有点复杂的数组和对象JSON. (2认同)
  • 如果你不想被某种类型的POJO束缚,这是非常好的. (2认同)
  • @Nil你不能通过改装发送json对象...你坚持使用pojo或我的答案......这是改造的本质.如果你想要更多关于这个请问Jake Wharton他是改装的开发人员,他的答案也可以与pojo一起使用. (2认同)

Tom*_*ySM 137

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

使用Retrofit2:

我昨晚遇到了这个问题,从Volley迁移到了Retrofit2(并且作为OP状态,这是直接进入Volley的JsonObjectRequest),虽然Jake的答案是Retrofit1.9的正确答案,但Retrofit2没有TypedString.

我的情况需要发送一个Map<String,Object>可以包含一些空值的转换为JSONObject(不会飞@FieldMap,不会有特殊字符,有些转换),所以关注@bnorms提示,并按照Square所述:

可以指定一个对象用作带有@Body注释的HTTP请求主体.

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

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

在你的界面使用@BodyRequestBody

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

在你的调用点创建一个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...
        }
    });
Run Code Online (Sandbox Code Playgroud)

希望这有助于任何人!


上面的优雅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())

}
Run Code Online (Sandbox Code Playgroud)

  • 这应该有更多的赞成 (4认同)
  • 是的,我看到了很多复杂的回应.如果您正在使用Retrofit2并想要使用volley的`JsonObjectRequest`,那么您需要做的就是这个.好答案. (2认同)
  • Retrofit将一个名为“ nameValuePairs”的键添加到所有json对象的顶部。我如何删除此@TommySM (2认同)

Jon*_*tos 73

Retrofit2中,如果要以raw 格式发送参数,则必须使用Scalars.

首先在你的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'
Run Code Online (Sandbox Code Playgroud)

你的界面

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

}
Run Code Online (Sandbox Code Playgroud)

活动

   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", "sample@gmail.com");
            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) {
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这里的技巧是Gson之前的Scalar适配器,否则Gson会将您手动序列化的JSON包装在String中. (8认同)
  • [jonathan-nolasco-barrientos](/sf/users/287266871/)您必须将.baseUrl(ApiInterface.ENDPOINT)更改为.baseUrl(ApiInterface.URL_BASE) (2认同)
  • 当你使用 `GsonConverterFactory` 时,`.toString()` 不是必需的。您可以使用`JsonObject` 而不是`JSONObject` 声明`Call&lt;User&gt; getUser(@Body JsonObject body);` 并直接传递`paramObject`。它会工作得很好。 (2认同)

sup*_*ser 39

使用JsonObject是这样的:

  1. 像这样创建你的界面:

    public interface laInterfaz{ 
        @POST("/bleh/blah/org")
        void registerPayer(@Body JsonObject bean, Callback<JsonObject> callback);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 使JsonObject符合jsons结构.

    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*/
    
    Run Code Online (Sandbox Code Playgroud)
  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());
        }
    };
    
    Run Code Online (Sandbox Code Playgroud)

那就是它!在我个人看来,它比制作pojos和处理课堂混乱要好得多.这更清洁了.

  • 这是最灵活的方式.你可以构建你的json对象,即使你不知道你将拥有多少个字段,或者即使你不知道它们的名字来自我+1 (2认同)

zer*_*dth 10

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

您还可以选择在Re​​trofit API中的JSON POST方法中添加标题注释...

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

...但是使用子类更明显是自我记录.

@POST("/json/foo/bar")
Response fubar( @Body TypedJsonString jsonBody ) ;
Run Code Online (Sandbox Code Playgroud)


Adi*_*dil 9

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'
Run Code Online (Sandbox Code Playgroud)

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


}
Run Code Online (Sandbox Code Playgroud)

3)从Json schema 2 pojo制作bean类

记住
-Target语言:Java -Source类型:JSON -Annotation样式:Gson -select 包含getter和setter - 您也可以选择Allow additional properties

http://www.jsonschema2pojo.org/

4)为api调用创建接口

    public interface Webservices {

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

}
Run Code Online (Sandbox Code Playgroud)

如果你有一个表单数据参数,那么添加下面的行

@Headers("Content-Type: application/x-www-form-urlencoded")
Run Code Online (Sandbox Code Playgroud)

form-data参数的其他方式检查此链接

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;
}
Run Code Online (Sandbox Code Playgroud)

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();
    }
}
Run Code Online (Sandbox Code Playgroud)


per*_*ser 8

我发现,当您使用复合对象作为@Body参数时,它不能与Retrofit配合使用GSONConverter(假设您正在使用该对象)。您不必使用它,JsonObject而不必JSONObject在使用它时就添加它,NameValueParams而不必太冗长-您只能看到,如果添加了日志记录拦截器的其他依赖项以及其他恶作剧。

因此,我发现解决此问题的最佳方法是使用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())
Run Code Online (Sandbox Code Playgroud)

这是电话:

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

  • 很好,这对我过夜搜索后有所帮助。 (2认同)

ali*_*tar 7

添加ScalarsConverterFactory进行改造:

在gradle中:

implementation'com.squareup.retrofit2:converter-scalars:2.5.0'
Run Code Online (Sandbox Code Playgroud)

您的改造:

retrofit = new Retrofit.Builder()
            .baseUrl(WEB_DOMAIN_MAIN)
            .addConverterFactory(ScalarsConverterFactory.create())
            .addConverterFactory(GsonConverterFactory.create(gson))
            .build();
Run Code Online (Sandbox Code Playgroud)

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

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

现在您可以发布原始json。


jat*_*ana 6

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

HashMap<String,String> hashMap=new HashMap<>();
        hashMap.put("email","this@gmail.com");
        hashMap.put("password","1234");
Run Code Online (Sandbox Code Playgroud)

然后像这样发送

Call<JsonElement> register(@Body HashMap registerApiPayload);
Run Code Online (Sandbox Code Playgroud)


Jim*_*bdi 6

这就是当前版本retrofit 2.6.2对我有用的方法

首先,我们需要在 Gradle 依赖项列表中添加一个 Scalars Converter,它将负责将 java.lang.String 对象转换为 text/plain 请求体,

implementation'com.squareup.retrofit2:converter-scalars:2.6.2'
Run Code Online (Sandbox Code Playgroud)

然后,我们需要将转换器工厂传递给我们的 Retrofit 构建器。稍后它会告诉 Retrofit 如何转换传递给服务的 @Body 参数。

private val retrofitBuilder: Retrofit.Builder by lazy {
    Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(ScalarsConverterFactory.create())
        .addConverterFactory(GsonConverterFactory.create())
}
Run Code Online (Sandbox Code Playgroud)

注意:在我的改造构建器中,我有两个转换器GsonScalars您可以同时使用它们,但要发送 Json 正文,我们需要关注,Scalars因此如果您不需要Gson将其删除

然后使用 String body 参数改造服务。

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

然后创建 JSON 正文

val user = JsonObject()
 user.addProperty("id", 001)
 user.addProperty("name", "Name")
Run Code Online (Sandbox Code Playgroud)

致电您的服务

RetrofitService.myApi.saveUser(user.toString())
Run Code Online (Sandbox Code Playgroud)


uma*_*151 5

经过这么多努力,发现基本的区别是您需要发送而JsonObject不是作为JSONObject参数。


小智 5

使用以下发送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());
Run Code Online (Sandbox Code Playgroud)

并将其传递给 url

@Body RequestBody key
Run Code Online (Sandbox Code Playgroud)


Sof*_*ner 5

如果您不想创建额外的类或使用JSONObject您可以使用HashMap.

改造接口:

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

称呼:

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

retrofit.create(TheApi::class.java)
     .signUp(map)
     .enqueue(callback)
Run Code Online (Sandbox Code Playgroud)


Dar*_*kin 5

在 Retrofit 中发送原始 json 所需的东西。

1)确保添加以下标题并删除任何其他重复的标题。因为,在 Retrofit 的官方文档中,他们特别提到-

请注意,标头不会相互覆盖。所有具有相同名称的标头都将包含在请求中。

@Headers({"Content-Type: application/json"})
Run Code Online (Sandbox Code Playgroud)

2) 一个。如果您使用的是转换器工厂,您可以将您的 json 作为字符串、JSONObject、JsonObject 甚至 POJO 传递。也查了一下,有ScalarConverterFactory没有必要只做GsonConverterFactory工作。

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

2) b. 如果您不使用任何转换器工厂,那么您必须使用 okhttp3 的 RequestBody 作为 Retrofit 的文档说-

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

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);
Run Code Online (Sandbox Code Playgroud)

3)成功!!