Ich verwende die effiziente Netzwerkbibliothek für die Nachrüstung, kann jedoch Dynamic JSON nicht verarbeiten, das ein einzelnes Präfix enthält, responseMessage
das sich object
zufällig responseMessage
ändert. In einigen Fällen ändert sich dasselbe Präfix ( ) in String (dynamisch).
Json-Format Objekt der AntwortMessage:
{
"applicationType":"1",
"responseMessage":{
"surname":"Jhon",
"forename":" taylor",
"dob":"17081990",
"refNo":"3394909238490F",
"result":"Received"
}
}
responseMessage
Das Json-Format ändert sich dynamisch in den Typ string:
{
"applicationType":"4",
"responseMessage":"Success"
}
Mein Problem ist JSON
, dass ich, da die Nachrüstung über eine integrierte Analyse verfügt, pro Anfrage ein einzelnes POJO zuweisen muss! Leider basiert die REST-API auf dynamischen JSON
Antworten. Das Präfix ändert sich sowohl bei erfolgreichen (...) als auch bei fehlgeschlagenen (...) Methoden zufällig von Zeichenfolge zu Objekt !
void doTrackRef(Map<String, String> paramsref2) {
RestAdapter restAdapter = new RestAdapter.Builder().setEndpoint("http://192.168.100.44/RestDemo").build();
TrackerRefRequest userref = restAdapter.create(TrackerRefRequest.class);
userref.login(paramsref2,
new Callback<TrackerRefResponse>() {
@Override
public void success(
TrackerRefResponse trackdetailresponse,
Response response) {
Toast.makeText(TrackerActivity.this, "Success",
Toast.LENGTH_SHORT).show();
}
@Override
public void failure(RetrofitError retrofitError) {
Toast.makeText(TrackerActivity.this, "No internet",
Toast.LENGTH_SHORT).show();
}
});
}
Pojo:
public class TrackerRefResponse {
private String applicationType;
private String responseMessage; //String type
//private ResponseMessage responseMessage; //Object of type ResponseMessage
//Setters and Getters
}
Im obigen Code ist das POJO TrackerRefResponse.java-Präfix responseMessage auf einen String oder ein Objekt vom Typ responseMessage festgelegt, sodass wir das POJO mit der Ref-Variablen mit demselben Namen erstellen können (Java-Grundlagen :)). Daher suche ich nach derselben Lösung für Dynamic JSON
in Retrofit. Ich weiß, dass dies bei normalen http-Clients mit asynchroner Aufgabe sehr einfach ist, aber es ist nicht die beste Vorgehensweise beim JSON
Parsen von REST-Api ! Betrachtet man die Leistung Benchmarks immer Volley oder Retrofit ist die beste Wahl, aber ich bin nicht in der Lage, dynamisch zu handhaben JSON
!
Mögliche Lösung, die ich kenne
Verwenden Sie die alte Asyc-Task beim Parsen des http-Clients. :(
Versuchen Sie, den RESTapi-Backend-Entwickler zu überzeugen.
Benutzerdefinierten Retrofit-Client erstellen :)
Antworten:
Spät zur Party, aber Sie können einen Konverter verwenden.
RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://graph.facebook.com") .setConverter(new DynamicJsonConverter()) // set your static class as converter here .build(); api = restAdapter.create(FacebookApi.class);
Dann verwenden Sie eine statische Klasse, die den Nachrüstkonverter implementiert:
static class DynamicJsonConverter implements Converter { @Override public Object fromBody(TypedInput typedInput, Type type) throws ConversionException { try { InputStream in = typedInput.in(); // convert the typedInput to String String string = fromStream(in); in.close(); // we are responsible to close the InputStream after use if (String.class.equals(type)) { return string; } else { return new Gson().fromJson(string, type); // convert to the supplied type, typically Object, JsonObject or Map<String, Object> } } catch (Exception e) { // a lot may happen here, whatever happens throw new ConversionException(e); // wrap it into ConversionException so retrofit can process it } } @Override public TypedOutput toBody(Object object) { // not required return null; } private static String fromStream(InputStream in) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(in)); StringBuilder out = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { out.append(line); out.append("\r\n"); } return out.toString(); } }
Ich habe diesen Beispielkonverter so geschrieben, dass er die Json-Antwort entweder als String, Object, JsonObject oder Map <String, Object> zurückgibt. Offensichtlich funktionieren nicht alle Rückgabetypen für jeden Json, und es gibt sicher Raum für Verbesserungen. Es zeigt jedoch, wie mit einem Konverter fast jede Antwort in dynamisches Json konvertiert wird.
quelle
RestAdapter
dieses Beispiel ist für Retrofit 1. Wie würden Sie den gleichen Wandler in Retrofit 2 implementieren?RestClient.java
import retrofit.client.Response; public interface RestClient { @GET("/api/foo") Response getYourJson(); }
YourClass.java
RestClient restClient; // create your restClient Response response = restClient.getYourJson(); Gson gson = new Gson(); String json = response.getBody().toString(); if (checkResponseMessage(json)) { Pojo1 pojo1 = gson.fromJson(json, Pojo1.class); } else { Pojo2 pojo2 = gson.fromJson(json, Pojo2.class); }
Sie müssen die Methode "checkResponseMessage" implementieren.
quelle
Versuchen Sie die benutzerdefinierte Deserialisierung
gson-converter
wie folgt (aktualisierte Antwort für Retrofit 2.0)Erstellen Sie drei Modelle wie unten gezeigt
ResponseWrapper
public class ResponseWrapper { @SerializedName("applicationType") @Expose private String applicationType; @SerializedName("responseMessage") @Expose private Object responseMessage; public String getApplicationType() { return applicationType; } public void setApplicationType(String applicationType) { this.applicationType = applicationType; } public Object getResponseMessage() { return responseMessage; } public void setResponseMessage(Object responseMessage) { this.responseMessage = responseMessage; } }
ResponseMessage
public class ResponseMessage extends ResponseWrapper { @SerializedName("surname") @Expose private String surname; @SerializedName("forename") @Expose private String forename; @SerializedName("dob") @Expose private String dob; @SerializedName("refNo") @Expose private String refNo; @SerializedName("result") @Expose private String result; public String getSurname() { return surname; } public void setSurname(String surname) { this.surname = surname; } public String getForename() { return forename; } public void setForename(String forename) { this.forename = forename; } public String getDob() { return dob; } public void setDob(String dob) { this.dob = dob; } public String getRefNo() { return refNo; } public void setRefNo(String refNo) { this.refNo = refNo; } public String getResult() { return result; } public void setResult(String result) { this.result = result; } }
ResponseString
public class ResponseString extends ResponseWrapper { }
UserResponseDeserializer (benutzerdefinierter Deserialiser)
public class UserResponseDeserializer implements JsonDeserializer<ResponseWrapper> { @Override public ResponseWrapper deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { if (((JsonObject) json).get("responseMessage") instanceof JsonObject){ return new Gson().fromJson(json, ResponseMessage.class); } else { return new Gson().fromJson(json, ResponseString.class); } } }
Retrofit 2.0-Implementierung
Gson userDeserializer = new GsonBuilder().setLenient().registerTypeAdapter(ResponseWrapper.class, new UserResponseDeserializer()).create(); Retrofit retrofit = new Retrofit.Builder() .baseUrl("base_url") .addConverterFactory(GsonConverterFactory.create(userDeserializer)) .build(); UserService request = retrofit.create(UserService.class); Call<ResponseWrapper> call1=request.listAllUsers(); call1.enqueue(new Callback<ResponseWrapper>() { @Override public void onResponse(Call<ResponseWrapper> call, Response<ResponseWrapper> response) { ResponseWrapper responseWrapper=response.body(); Log.i("DYNAMIC RESPONSE", String.valueOf(response.body().getResponseMessage())); } @Override public void onFailure(Call<ResponseWrapper> call, Throwable t) { } });
Verwendete Bibliotheken
***** Vorherige Antwort (obige Antwort wird eher empfohlen) *****
Ändere dein Pojo so
public class TrackerRefResponse { private String applicationType; private Object responseMessage; public Object getResponseMessage() { return responseMessage; } public void setResponseMessage(Object responseMessage) { this.responseMessage = responseMessage; } }
und ändern Sie die onResponse der Nachrüstung wie folgt
@Override public void onResponse(Response<TrackerRefResponse > response) { if (response.isSuccess()) { if (response.getResponseMessage() instanceof String) { handleStringResponse(); } else { handleObjectResponse(); } } }
Sie können diesen Beitrag auch überprüfen, um weitere Informationen zum dynamischen JSON-Parsing zu erhalten
quelle
Die akzeptierte Antwort schien mir zu kompliziert, ich löse sie folgendermaßen:
Call<ResponseBody> call = client.request(params); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Response<ResponseBody> response) { if (response.isSuccess()) { Gson gson = new Gson(); ResponseBody repsonseBody = response.body().string(); if (isEmail) { EmailReport reports = gson.fromJson(responseBody, EmailReport.class); } else{ PhoneReport reports = gson.fromJson(repsonseBody, PhoneReport.class); } } } @Override public void onFailure(Throwable t) { Log.e(LOG_TAG, "message =" + t.getMessage()); } });
Dies ist nur ein Beispiel, um Ihnen zu zeigen, wie Sie verschiedene Modelle verwenden können.
Die Variable
isEmail
ist nur ein Boolescher Wert für Ihre Bedingung, um das entsprechende Modell zu verwenden.quelle
Jede Ihrer möglichen Lösungen wird funktionieren. Sie können auch den Rückgabetyp der Retrofit-API-Schnittstelle an die Antwort senden. Mit dieser Antwort erhalten Sie einen Text,
Inputstream
den Sie in ein JSON-Objekt konvertieren und nach Belieben lesen können.Schauen Sie unter: http://square.github.io/retrofit/#api-declaration - unter RESPONSE OBJECT TYPE
Aktualisiert
Retrofit 2 ist ab sofort erhältlich und bringt einige Änderungen an der Dokumentation und Bibliothek mit sich.
Unter http://square.github.io/retrofit/#restadapter-configuration können Anforderungs- und Antworttextobjekte verwendet werden.
quelle
Ich weiß, dass ich sehr, sehr spät zur Party komme. Ich hatte ein ähnliches Problem und löste es einfach so:
public class TrackerRefResponse { private String applicationType; // Changed to Object. Works fine with String and array responses. private Object responseMessage; }
Ich habe buchstäblich nur geändert, um in Objekt zu tippen. Ich habe mich für diesen Ansatz entschieden, weil nur ein Feld in der Antwort dynamisch war (für mich war meine Antwort viel komplizierter), sodass die Verwendung eines Konverters das Leben erschwert hätte. Verwendet Gson, um von dort aus mit dem Objekt zu arbeiten, je nachdem, ob es sich um einen String- oder Array-Wert handelt.
Hoffe das hilft jemandem auf der Suche nach einer einfachen Antwort :).
quelle
Wenn es nicht möglich wäre, die Backend-API zu ändern, würde ich die folgenden Varianten in Betracht ziehen (wenn Gson zum Konvertieren von JSON verwendet wird).
Wir können Adapter vom Typ Gson verwenden , um einen benutzerdefinierten Adapter für den
ResponseMessage
Typ zu erstellen , der dynamisch entscheidet, wie der eingehende JSON analysiert wird (unter Verwendung von etwas wieif (reader.peek() == JsonToken.STRING)
).Fügen Sie einige Metainformationen, die den Antworttyp beschreiben, in einen HTTP-Header ein und bestimmen Sie daraus, welche Typinformationen an die Gson-Instanz gesendet werden müssen.
quelle
Zusätzlich zu dem, was du erzählt hast -
Rückruf verwenden Anschließend können Sie die Felder mit der regulären Methode get abrufen. Weitere Informationen finden Sie im Javadoc von Gson.
http://google-gson.googlecode.com/svn/tags/1.2.3/docs/javadocs/com/google/gson/JsonObject.html
quelle
Ich bin auch von diesem Problem ausgegangen. aber ich bin nicht sicher, ob dies Ihr Fall war (ich benutze Retrofit2)
In meinem Fall muss ich Fehler- und Erfolgsmeldungen behandeln.
Auf Erfolg
{ "call_id": 1, "status": "SUCCESS", "status_code": "SUCCESS", "result": { "data1": { "id": "RFP2UjW7p8ggpMXzYO9tRg==", "name": "abcdef", "mobile_no": "96655222", "email": "" }, "data2": [ { "no": "12345" }, { "no": "45632" } ] } }
Bei Fehler,
{ "call_id": 1, "status": "FAILED", "status_code": "NO_RECORDS", "error": { "error_title": "xxx", "error_message": "details not found" } }
dafür habe ich gerade ein anderes POJO erstellt
Error
,public class ValidateUserResponse { @SerializedName("call_id") public String callId; @SerializedName("status") public String status; @SerializedName("status_code") public String statusCode; @SerializedName("result") public ValidateUserResult result; @SerializedName("error") public Error error; }
Error.java
public class Error { @SerializedName("error_title") public String errorTitle; @SerializedName("error_message") public String errorMessage; }
ValidateUser.java
public class ValidateUserResult { @SerializedName("auth_check") public String authCheck; @SerializedName("data1") public Data1 data1; @SerializedName("data2") public List<Data2> data2; }
im obigen Fall, wenn der
result
Schlüssel auf json data1, data2 enthält, wird derValidateUserResult.java
initialisiert. Wenn ein Fehler auftritt, wird dieError.java
Klasse initialisiert.quelle
Ich weiß, dass ich zu spät komme, aber ich möchte nur meine Gedanken teilen. Ich habe an einem Projekt gearbeitet, in dem ich eine Methode schreibe. Die Methode verwendet die Nachrüstung, um Daten vom Server abzurufen. Da andere Entwickler in meinem Unternehmen diese Methode verwenden, konnte ich keine
POJO
Klasse verwenden (in Ihrem Beispiel dieTrackerRefResponse
Klasse). Also habe ich folgendes benutztJsonObject
/Object
mögen:Schnittstelle APIService.java
public class APIService{ @FormUrlEncoded @POST Call<JsonObject> myPostMethod(@Url String url, @Field("input") String input); }
Dann schrieb ich in meiner Methode Folgendes:
Call<JsonObject> call = RetrofitClient.getAPIService().establishUserSession(post_request_url, someParameter); call.enqueue(new Callback<JsonObject>() { @Override public void onResponse(Call<JsonObject> call, Response<JsonObject> response) { JsonObject jsonObject = response.body(); String jsonString = response.body().toString(); // then do your stuff. maybe cast to object using a factory pattern } // rest of the code }
Sie können auch
Object
anstelle von 'JsonObject' verwenden. Wenn Sie später wissen, um welche Art von Antwort es sich handelt, können Sie diese möglicherweise in das gewünschte Objekt umwandeln.quelle