wake-up-neo.com

Wie man POST Rohes JSON im Rumpf einer Retrofit-Anforderung?

Diese Frage wurde vielleicht schon einmal gestellt, aber sie wurde nicht definitiv beantwortet. Wie genau kann ein vollständiger JSON in den Rumpf einer Retrofit-Anforderung geschrieben werden?

Siehe ähnliche Frage hier . Oder ist diese Antwort richtig, dass sie als URL - codiert und als Feld übergeben werden muss ? Ich hoffe wirklich nicht, da die Dienste, mit denen ich mich verbinde, nur rohen JSON im Post-Body erwarten. Sie sind nicht dazu eingerichtet, nach einem bestimmten Feld für die JSON-Daten zu suchen.

Ich möchte das mit den restperts ein für alle Mal klarstellen. Eine Person hat geantwortet, Retrofit nicht zu verwenden. Der andere war sich der Syntax nicht sicher. Ein anderer meint ja, es kann aber nur gemacht werden, wenn sein Formular url-kodiert und in ein Feld gestellt wird (das ist in meinem Fall nicht akzeptabel). Nein, ich kann nicht alle Dienste für meinen Android-Client neu codieren. Und ja, in großen Projekten ist es üblich, reine JSON zu posten, anstatt JSON-Inhalte als Feldeigenschaftswerte zu übergeben. Lasst es uns richtig machen und weitermachen. Kann jemand auf die Dokumentation oder ein Beispiel verweisen, das zeigt, wie dies gemacht wird? Oder geben Sie einen gültigen Grund dafür an, warum dies nicht möglich ist/ist.

UPDATE: Eines kann ich mit hundertprozentiger Sicherheit sagen. Sie können dies in Googles Volley tun. Es ist direkt eingebaut. Können wir das in Retrofit tun?

215
user3243335

Die Annotation @Body definiert einen einzelnen Anforderungshauptteil.

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

Da Retrofit standardmäßig Gson verwendet, werden die FooRequest-Instanzen als einziger Hauptteil der Anforderung als JSON serialisiert.

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

  FooRequest(String foo, String bar) {
    this.foo = foo;
    this.bar = bar;
  }
}

Anrufen mit:

FooResponse = foo.postJson(new FooRequest("kit", "kat"));

Wird den folgenden Körper ergeben:

{"foo":"kit","bar":"kat"}

Die Gson-Dokumente haben viel mehr darüber, wie die Objektserialisierung funktioniert.

Wenn Sie wirklich "rohen" JSON als Körper selbst senden möchten (verwenden Sie hierfür bitte Gson!), Können Sie trotzdem TypedInput verwenden:

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

TypedInput ist definiert als "Binärdaten mit einem zugeordneten MIME-Typ." Es gibt zwei Möglichkeiten, Rohdaten mit der obigen Deklaration einfach zu senden:

  1. Verwenden Sie TypedByteArray , um unformatierte Bytes und den JSON-Mime-Typ zu senden:

    String json = "{\"foo\":\"kit\",\"bar\":\"kat\"}";
    TypedInput in = new TypedByteArray("application/json", json.getBytes("UTF-8"));
    FooResponse response = foo.postRawJson(in);
    
  2. Unterklasse TypedString , um eine TypedJsonString-Klasse zu erstellen:

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

    Und dann verwenden Sie eine Instanz dieser Klasse, ähnlich zu # 1.

393
Jake Wharton

Ja, ich weiß, dass es spät ist, aber jemand würde wahrscheinlich davon profitieren.

Retrofit2 verwenden:

Ich bin gestern Abend auf dieses Problem gestoßen, als ich von Volley auf Retrofit2 umgestiegen bin (und wie OP angibt, wurde dies mit JsonObjectRequest direkt in Volley eingebaut), und obwohl Jakes Antwort die richtige ist Für Retrofit1.9 verfügt Retrofit2 nicht über TypedString.

In meinem Fall musste ein Map<String,Object> Gesendet werden, der einige Nullwerte enthalten konnte, die in ein JSONObject konvertiert wurden (das nicht mit @FieldMap Fliegt, und spezielle Zeichen, einige werden konvertiert), also folgen Sie @bnorms Hinweis und wie angegeben durch Quadrat :

Ein Objekt kann zur Verwendung als HTTP-Anforderungshauptteil mit der Annotation @Body angegeben werden.

Das Objekt wird auch mit einem in der Retrofit-Instanz angegebenen Konverter konvertiert. Wenn kein Konverter hinzugefügt wird, kann nur RequestBody verwendet werden.

Dies ist also eine Option, die RequestBody und ResponseBody verwendet:

Verwenden Sie in Ihrer Schnittstelle @Body Mit RequestBody

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

Erstellen Sie in Ihrem Aufrufpunkt einen RequestBody, der MediaType angibt, und konvertieren Sie Ihre Map mit JSONObject in das richtige 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...
        }
    });

Hoffe das hilft jedem!


Eine elegante Kotlin-Version des oben genannten, um die Parameter aus der JSON-Konvertierung in den Rest Ihres Anwendungscodes abstrahieren zu können:

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

}
134
TommySM

Anstelle von Klassen können wir HashMap<String, Object> auch direkt verwenden, um beispielsweise Körperparameter Zu senden

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

In Retrofit2 , Wenn Sie Ihre Parameter in RAW senden möchten, müssen Sie Scalars verwenden.

füge dies zuerst in deinem gradle hinzu:

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'

Deine Schnittstelle

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

}

Aktivität

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

Die Verwendung von JsonObject ist wie es ist: 

  1. Erstellen Sie Ihre Schnittstelle folgendermaßen:

    public interface laInterfaz{ 
        @POST("/bleh/blah/org")
        void registerPayer(@Body JsonObject bean, Callback<JsonObject> callback);
    }
    
  2. Machen Sie das JsonObject entsprechend der Jsons-Struktur. 

    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. Rufen Sie den Dienst an: 

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

Und das ist es! Meiner Meinung nach ist es viel besser, als Pojos herzustellen und mit dem Klassen-Chaos zu arbeiten. Das ist viel sauberer.

34
superUser

Ich mag besonders Jakes Vorschlag der TypedString-Unterklasse oben . Sie könnten in der Tat eine Vielzahl von Unterklassen erstellen, die auf den Arten von POST - Daten basieren, die Sie als Push-Up planen, und jede davon mit einem eigenen Satz von konsistenten Anpassungen.

Sie haben auch die Möglichkeit, Ihren JSON-Methoden POST in Ihrer Retrofit-API eine Kopfanmerkung hinzuzufügen.

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

… Aber die Verwendung einer Unterklasse ist offensichtlich selbstdokumentierend.

@POST("/json/foo/bar")
Response fubar( @Body TypedJsonString jsonBody ) ;
8
zerobandwidth

1) Abhängigkeiten hinzufügen-

 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) Api-Handler-Klasse machen 

    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) Bean-Klassen aus Json Schema 2 Pojo erstellen

Merken
-Zielsprache: Java -Squellentyp: JSON - Anmerkungsstil: Gson -Wählen Sie Include-Getter und Setter -sie können auch auswählen Zusätzliche Eigenschaften zulassen

http://www.jsonschema2pojo.org/

4) Schnittstelle zum API-Aufruf machen

    public interface Webservices {

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

}

wenn Sie über Formulardatenparameter verfügen, fügen Sie unterhalb der Zeile hinzu

@Headers("Content-Type: application/x-www-form-urlencoded")

Andere Möglichkeit für Formulardatenparameter: this link

5) JsonObject zur Übergabe in body als Parameter erstellen

 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) Api so nennen

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

Nach so viel Aufwand stellte sich heraus, dass der grundlegende Unterschied darin besteht, dass Sie die JsonObject anstelle von JSONObject als Parameter senden.

4
umair151

Basierend auf der ersten Antwort habe ich eine Lösung, um nicht für jede Anfrage POJOs erstellen zu müssen.

Zum Beispiel möchte ich diese JSON posten.

{
    "data" : {
        "mobile" : "qwer",
        "password" : "qwer"
    },
    "commom" : {}
}

dann erstelle ich eine gemeinsame Klasse wie folgt:

import Java.util.Map;
import Java.util.HashMap;

public class WRequest {

    Map<String, Object> data;
    Map<String, Object> common;

    public WRequest() {
        data = new HashMap<>();
        common = new HashMap<>();
    }
}

Endlich, wenn ich einen Json brauche

WRequest request = new WRequest();
request.data.put("type", type);
request.data.put("page", page);

Die mit Annotation gekennzeichnete Anforderung @Body kann dann an Retrofit übergeben werden.

3
wjploop

verwenden Sie folgendes, um Json zu senden

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

und übergeben Sie es an URL

@Body RequestBody key
2
Mahesh Pandit

Wenn Sie keine zusätzlichen Klassen erstellen oder JSONObject verwenden möchten, können Sie eine HashMap verwenden.

Retrofit-Schnittstelle:

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

Anruf:

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

retrofit.create(TheApi::class.Java)
     .signUp(map)
     .enqueue(callback)
2
SoftDesigner

Hinzufügen von ScalarsConverterFactory zum Nachrüsten:

in gradle:

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

ihre Nachrüstung:

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

Ändern Sie den @Body-Parameter der Aufrufschnittstelle in String, und fügen Sie @Headers("Content-Type: application/json") hinzu:

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

jetzt kannst du rohen Json posten.

2
ali-star

Ich fand, dass, wenn Sie ein zusammengesetztes Objekt als @Body-Parameter verwenden, es mit GSONConverter des Retrofit nicht gut funktionieren könnte (unter der Annahme, dass Sie das verwenden) . Sie müssen JsonObject und nicht JSONObject verwenden, wenn Sie damit arbeiten NameValueParams ohne sich darüber klar zu sein - Sie können dies nur sehen, wenn Sie eine weitere Abhängigkeit des Abfangjägers für die Protokollierung und anderer Phänomene hinzufügen.

Was ich für die beste Lösung gefunden habe, ist die Verwendung von RequestBody. Sie wenden Ihr Objekt mit einem einfachen api-Aufruf zu RequestBody an und starten es .. In meinem Fall konvertiere ich eine Karte: 

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

und das ist der Aufruf:

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

Für mehr Klarheit bei den hier gegebenen Antworten können Sie auf diese Weise die Erweiterungsfunktionen verwenden. Dies ist nur, wenn Sie Kotlin verwenden

Wenn Sie com.squareup.okhttp3:okhttp:4.0.1 verwenden, sind die älteren Methoden zum Erstellen von Objekten mit MediaType und RequestBody veraltet und können in nicht mehr verwendet werden. Kotlin .

Wenn Sie die Erweiterungsfunktionen verwenden möchten, um ein MediaType -Objekt und ein ResponseBody -Objekt aus Ihren Zeichenfolgen abzurufen, fügen Sie der Klasse, in der Sie sie voraussichtlich verwenden, zunächst die folgenden Zeilen hinzu .

import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody

Auf diese Weise können Sie jetzt direkt ein Objekt von MediaType erhalten

val mediaType = "application/json; charset=utf-8".toMediaType()

Um ein Objekt von RequestBody zu erhalten, konvertieren Sie zunächst das zu sendende JSONObject auf diese Weise in einen String. Sie müssen das mediaType-Objekt übergeben.

val requestBody = myJSONObject.toString().toRequestBody(mediaType)
1
Devenom

Ich habe folgendes ausprobiert: Wenn Sie Ihre Retrofit-Instanz erstellen, fügen Sie diese Konverter-Factory zum Retrofit-Builder hinzu:

gsonBuilder = new GsonBuilder().serializeNulls()     
your_retrofit_instance = Retrofit.Builder().addConverterFactory( GsonConverterFactory.create( gsonBuilder.create() ) )
1

Sie können Hashmap verwenden, wenn Sie nicht für jeden API-Aufruf eine Pojo-Klasse erstellen möchten.

HashMap<String,String> hashMap=new HashMap<>();
        hashMap.put("email","[email protected]");
        hashMap.put("password","1234");

Und dann so senden

Call<JsonElement> register(@Body HashMap registerApiPayload);
0
jatin rana