programing tip

Retrofit에서 동적 JSON을 처리하는 방법은 무엇입니까?

itbloger 2020. 10. 22. 07:45
반응형

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를 생성 할 수 있으므로 JSONRetrofit에서 dynamic 대한 동일한 솔루션을 찾고 있습니다. 비동기 작업을 사용하는 일반 http 클라이언트에서는 이것이 매우 쉬운 작업이라는 것을 알고 있지만 REST-Api JSON구문 분석 에서는 모범 사례가 아닙니다 ! 성능 벤치 마크를 보면 항상 Volley 또는 Retrofit이 최선의 선택이지만 동적 핸들링에 실패했습니다 JSON!

내가 아는 가능한 해결책

  1. http 클라이언트 구문 분석과 함께 이전 asyc 작업을 사용하십시오. :(

  2. RESTapi 백엔드 개발자를 설득하십시오.

  3. 사용자 지정 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).

  1. 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 like if (reader.peek() == JsonToken.STRING)).

  2. 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

반응형