Retrofit에서 동적 JSON을 처리하는 방법은 무엇입니까?
개조 효율적인 네트워킹 라이브러리를 사용하고 있지만 무작위로 responseMessage
변경되는 단일 접두사 를 포함하는 Dynamic JSON을 처리 할 수 없으며 object
, responseMessage
경우에 따라 (동적으로) 동일한 접두사 ( )가 String으로 변경됩니다.
responseMessage의 JSON 형식 객체 :
{
"applicationType":"1",
"responseMessage":{
"surname":"Jhon",
"forename":" taylor",
"dob":"17081990",
"refNo":"3394909238490F",
"result":"Received"
}
}
responseMessage
JSON 형식은 문자열 형식으로 동적으로 변경됩니다.
{
"applicationType":"4",
"responseMessage":"Success"
}
내 문제는 개조에 내장 JSON
구문 분석 기능이 있기 때문에 요청 당 단일 POJO를 할당해야한다는 것입니다! 하지만 REST-API는 안타깝게도 동적 JSON
응답을 기반으로 합니다. 접두사는 success (...) 및 failure (...) 메서드 에서 문자열에서 객체로 임의로 변경됩니다 !
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();
}
});
}
포조 :
public class TrackerRefResponse {
private String applicationType;
private String responseMessage; //String type
//private ResponseMessage responseMessage; //Object of type ResponseMessage
//Setters and Getters
}
위 코드에서 POJO TrackerRefResponse.java 접두사 responseMessage는 responseMessage 유형의 문자열 또는 객체로 설정되어 있으므로 동일한 이름 (java basics :))의 ref 변수로 POJO를 생성 할 수 있으므로 JSON
Retrofit에서 dynamic 에 대한 동일한 솔루션을 찾고 있습니다. 비동기 작업을 사용하는 일반 http 클라이언트에서는 이것이 매우 쉬운 작업이라는 것을 알고 있지만 REST-Api JSON
구문 분석 에서는 모범 사례가 아닙니다 ! 성능 벤치 마크를 보면 항상 Volley 또는 Retrofit이 최선의 선택이지만 동적 핸들링에 실패했습니다 JSON
!
내가 아는 가능한 해결책
http 클라이언트 구문 분석과 함께 이전 asyc 작업을 사용하십시오. :(
RESTapi 백엔드 개발자를 설득하십시오.
사용자 지정 Retrofit 클라이언트 만들기 :)
파티에 늦었지만 변환기를 사용할 수 있습니다.
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);
그런 다음 개조의 변환기를 구현하는 정적 클래스를 사용합니다.
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();
}
}
이 샘플 변환기를 작성하여 String, Object, JsonObject 또는 Map <String, Object>로 Json 응답을 반환합니다. 분명히 모든 반환 유형이 모든 Json에서 작동하는 것은 아니며 개선의 여지가 있습니다. 그러나 변환기를 사용하여 거의 모든 응답을 동적 Json으로 변환하는 방법을 보여줍니다.
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);
}
"checkResponseMessage"메소드를 구현해야합니다.
gson-converter
아래와 같이 사용자 지정 역 직렬화를 시도하십시오 (Retrofit 2.0에 대한 업데이트 된 답변).
아래와 같이 3 개의 모델을 생성합니다.
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 (사용자 지정 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 구현
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) {
}
});
사용 된 라이브러리
'com.squareup.retrofit2 : retrofit : 2.3.0'컴파일
'com.squareup.retrofit2 : converter-gson : 2.3.0'컴파일
***** 이전 답변 (위 답변이 더 권장 됨) *****
포조를 이렇게 변경하세요
public class TrackerRefResponse {
private String applicationType;
private Object responseMessage;
public Object getResponseMessage() {
return responseMessage;
}
public void setResponseMessage(Object responseMessage) {
this.responseMessage = responseMessage;
}
}
다음과 같이 개조의 onResponse를 변경하십시오.
@Override
public void onResponse(Response<TrackerRefResponse > response) {
if (response.isSuccess()) {
if (response.getResponseMessage() instanceof String)
{
handleStringResponse();
}
else
{
handleObjectResponse();
}
}
}
동적 json 구문 분석에 대한 자세한 내용은이 게시물을 확인할 수도 있습니다.
가능한 모든 솔루션이 작동합니다. 또한 할 수있는 일은 Retrofit api 인터페이스 반환 유형을 응답으로 보내는 것입니다. 이 응답 Inputstream
을 통해 JSON 객체로 변환하고 적절하다고 판단되는대로 읽을 수 있는 본문 을 얻습니다 .
http://square.github.io/retrofit/#api-declaration-RESPONSE OBJECT TYPE 아래를 보십시오.
업데이트 됨
Retrofit 2가 출시되었으며 설명서와 라이브러리가 일부 변경되었습니다.
에서 봐 http://square.github.io/retrofit/#restadapter-configuration는 요청 및 사용할 수있는 응답 본체의 객체가 있습니다.
받아 들여지는 대답은 나에게 복잡해 보였으며 다음과 같이 해결합니다.
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());
}
});
이것은 다른 모델을 사용하는 방법을 보여주기위한 예제 일뿐입니다.
변수 isEmail
는 조건이 적절한 모델을 사용하기위한 부울입니다.
파티에 너무 늦었다는 걸 알아요. 비슷한 문제가 있었고 다음과 같이 해결했습니다.
public class TrackerRefResponse {
private String applicationType;
// Changed to Object. Works fine with String and array responses.
private Object responseMessage;
}
문자 그대로 Object로 변경했습니다. 응답에서 단 하나의 필드 만 동적 이었기 때문에이 접근 방식을 선택했습니다 (저에게는 제 응답이 훨씬 더 복잡했습니다). 그래서 변환기를 사용하면 삶이 힘들었을 것입니다. Gson을 사용하여 String 또는 Array 값인지에 따라 거기에서 Object로 작업했습니다.
Hope this helps someone looking for a simple answer :).
If it was not possible to change the backend API, I would consider the following variants (if Gson is used to convert JSON).
We can use Gson type adapters to create a custom adapter for
ResponseMessage
type that dynamically decides how to parse the inoming JSON (using something likeif (reader.peek() == JsonToken.STRING)
).Put some meta information describing the response type into an HTTP header and use it to determine what type information must be fed to Gson instance.
In addition to what you told -
Use Callback Then you can retrieve the fields using regular get method. For more information, go through the javadoc of gson.
http://google-gson.googlecode.com/svn/tags/1.2.3/docs/javadocs/com/google/gson/JsonObject.html
I too ran of this issue. but i am not sure if this was your case , (i am using Retrofit2)
on my case i need to handle error, and success messages.
On Success
{
"call_id": 1,
"status": "SUCCESS",
"status_code": "SUCCESS",
"result": {
"data1": {
"id": "RFP2UjW7p8ggpMXzYO9tRg==",
"name": "abcdef",
"mobile_no": "96655222",
"email": ""
},
"data2": [
{
"no": "12345"
},
{
"no": "45632"
}
]
}
}
On Error,
{
"call_id": 1,
"status": "FAILED",
"status_code": "NO_RECORDS",
"error": {
"error_title": "xxx",
"error_message": "details not found"
}
}
for this i just created another POJO 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;
}
in the above case if the result
key on json contains data1,data2 then the ValidateUserResult.java
get initialised. if error then the Error.java
class get initialized.
참고URL : https://stackoverflow.com/questions/24279245/how-to-handle-dynamic-json-in-retrofit
'programing tip' 카테고리의 다른 글
R : 슬롯이란 무엇입니까? (0) | 2020.10.22 |
---|---|
copyWithZone 구현시 모범 사례 : (0) | 2020.10.22 |
호스트 이름의 유효한 문자? (0) | 2020.10.22 |
일반 배열에 대한 범위 기반은 어떻게 작동합니까? (0) | 2020.10.22 |
왜 이것을 람다에서 참조 ( '& this')로 캡처 할 수 없습니까? (0) | 2020.10.22 |