Làm thế nào để xử lý JSON động trong Trang bị thêm?


82

Tôi đang sử dụng trang bị thêm thư viện mạng hiệu quả, nhưng tôi không thể xử lý Dynamic JSON chứa tiền tố đơn lẻ responseMessagethay đổi thành objectngẫu nhiên, tiền tố tương tự ( responseMessage) thay đổi thành Chuỗi trong một số trường hợp (động).

Định dạng json Đối tượng của phản hồi

{
   "applicationType":"1",
   "responseMessage":{
      "surname":"Jhon",
      "forename":" taylor",
      "dob":"17081990",
      "refNo":"3394909238490F",
      "result":"Received"
   }

}

responseMessage Định dạng json tự động thay đổi thành chuỗi loại:

 {
       "applicationType":"4",
       "responseMessage":"Success"          
 }

Vấn đề của tôi là vì trang bị thêm đã JSONtích hợp phân tích cú pháp, tôi phải chỉ định một POJO cho mỗi yêu cầu! nhưng thật không may, REST-API được xây dựng dựa trên các JSONphản hồi động . Tiền tố sẽ thay đổi từ chuỗi sang đối tượng một cách ngẫu nhiên trong cả hai phương thức thành công (...)thất bại (...) !

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


}

Trong đoạn mã trên POJO TrackerRefResponse.java prefix responseMessage được đặt thành chuỗi hoặc đối tượng của kiểu responseMessage, vì vậy chúng tôi có thể tạo POJO với biến ref có cùng tên (java basics :)) vì vậy tôi đang tìm kiếm giải pháp tương tự cho động JSONtrong Retrofit. Tôi biết đây là công việc rất dễ dàng trong các ứng dụng khách http bình thường với tác vụ không đồng bộ, nhưng đó không phải là phương pháp hay nhất trong JSONphân tích cú pháp REST-Api ! nhìn vào hiệu suất Benchmarks luôn luôn là Volley hoặc Retrofit là lựa chọn tốt nhất, nhưng tôi không xử lý được động JSON!

Giải pháp khả thi mà tôi biết

  1. Sử dụng tác vụ asyc cũ với phân tích cú pháp máy khách http. :(

  2. Cố gắng thuyết phục nhà phát triển phụ trợ RESTapi.

  3. Tạo ứng dụng trang bị thêm tùy chỉnh :)


1
"Hãy cố gắng thuyết phục nhà phát triển phần phụ trợ RESTapi." đã lừa cho tôi! cười lớn! ;) (nb: Tôi cũng là nhà phát triển phụ trợ, tôi phải thuyết phục chính mình!)
mrx

Câu trả lời:


38

Đến muộn, nhưng bạn có thể sử dụng công cụ chuyển đổi.

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

Sau đó, bạn sử dụng một lớp tĩnh triển khai Bộ chuyển đổi của trang bị thêm:

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

Tôi đã viết bộ chuyển đổi mẫu này để nó trả về phản hồi Json dưới dạng Chuỗi, Đối tượng, JsonObject hoặc Bản đồ <Chuỗi, Đối tượng>. Rõ ràng là không phải tất cả các loại trả lại đều hoạt động với mọi Json, và chắc chắn có chỗ để cải thiện. Nhưng nó trình bày cách sử dụng Bộ chuyển đổi để chuyển đổi hầu hết mọi phản hồi sang Json động.


13
Xem RestAdapterví dụ này là dành cho Retrofit 1. Bạn sẽ triển khai cùng một bộ chuyển đổi như thế nào trong Retrofit 2?
androidtitan

1
ConversionException không khả dụng trong Retrofit 2 :(
mutkan

20

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

Bạn phải triển khai phương thức "checkResponseMessage".


Làm cách nào để làm điều tương tự trong Retrofit 2?
Vadim Kotov

"CheckResponseMessage" là gì?
Shashank singh

15

Hãy thử tùy chỉnh deserialisation bằng cách sử dụng gson-converternhư bên dưới (câu trả lời được cập nhật cho Retrofit 2.0)

Tạo ba mô hình như hình dưới đây

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

}

Phản hồi

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

}

Chuỗi phản hồi

public class ResponseString extends ResponseWrapper {

}

UserResponseDeserializer (tùy chỉnh 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);
        }

}
}

Triển khai 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) {
        }
    });

Thư viện đã sử dụng

biên dịch 'com.squareup.retrofit2: retrofit: 2.3.0'

biên dịch 'com.squareup.retrofit2: converter-gson: 2.3.0'

***** Câu trả lời trước (câu trả lời trên được khuyến nghị nhiều hơn) *****

Thay đổi pojo của bạn như thế này

public class TrackerRefResponse {

  private String applicationType;
  private Object responseMessage;

  public Object getResponseMessage() {
  return responseMessage;
  }

  public void setResponseMessage(Object responseMessage) {
  this.responseMessage = responseMessage;
 }
}

và thay đổi onResponse của trang bị thêm như thế này

 @Override
public void onResponse(Response<TrackerRefResponse > response) {
    if (response.isSuccess()) {
        if (response.getResponseMessage() instanceof String)
            {
            handleStringResponse();
         }
        else 
            {
            handleObjectResponse();
         }
    }
}

bạn cũng có thể xem bài đăng này để biết thêm chi tiết về phân tích cú pháp json động


Lớp ResponseWrapper có thực sự cần thiết không? Tôi nghĩ rằng nó trông rất khó hiểu. Tôi cần một chuyển đổi trên bất cứ điều gì nhưng đối tượng cao nhất trong hệ thống phân cấp ...
RabbitBones22

1
bạn có thể tránh được những lớp wrapper nếu khó hiểu của nó và cố gắng này freshbytelabs.com/2018/12/...
Navneet Krishna

9

Câu trả lời được chấp nhận có vẻ phức tạp đối với tôi, tôi giải quyết nó theo cách này:

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

Đây chỉ là một ví dụ nhằm cho bạn thấy cách bạn có thể sử dụng các mô hình khác nhau.

Biến isEmailchỉ là một boolean cho điều kiện của bạn để sử dụng mô hình thích hợp.


Bạn có thể vui lòng giải thích điều đó? Mã này không mang tính mô tả. MType đến từ đâu?
Desdroid

@Desdroid Tôi đã đơn giản hóa mã và sau đó mở rộng với giải thích
meda

2
Tuy nhiên, tôi tin rằng điều này sẽ không hữu ích nếu bạn không biết Loại phản hồi trước khi thực hiện cuộc gọi, đó là trường hợp của câu hỏi. Chắc chắn rằng trước tiên bạn có thể lấy Dòng đầu vào của Phần nội dung phản hồi, đọc một vài dòng, xác định Loại phần thân là và sau đó chuyển đổi nó. Nhưng nó không chỉ đơn giản như vậy.
Desdroid

Tôi đang tìm cách tốt nhất để xử lý các loại trả lại khác nhau. Câu trả lời của bạn trông khá hứa hẹn, nhưng tôi không chắc bạn biết Loại từ đâu. Đó là lý do tại sao tôi muốn bạn giải thích điều đó;)
Desdroid

2
Tôi nghĩ rằng nó sẽ không làm việc vì deserializer sẽ ném một ngoại lệ và đạt được onFailure () không onResponse ()
Amt87

9

Bất kỳ giải pháp khả thi nào của bạn sẽ hoạt động. Những gì bạn cũng có thể làm là gửi kiểu trả về giao diện api Retrofit để phản hồi. Với phản hồi đó, bạn sẽ nhận được một nội dung Inputstreammà bạn có thể chuyển đổi thành Đối tượng JSON và đọc khi bạn thấy phù hợp.

Xem tại: http://square.github.io/retrofit/#api-decosystem - trong LOẠI ĐỐI TƯỢNG PHẢN ỨNG

Đã cập nhật

Retrofit 2 hiện đã ra mắt và kèm theo đó là một số thay đổi đối với tài liệu và thư viện.

Hãy xem http://square.github.io/retrofit/#restadapter-configuration có đối tượng nội dung yêu cầu và phản hồi có thể được sử dụng.


6
Tôi e rằng tôi không thể tìm thấy phần bạn đã cung cấp, có bất kỳ từ đồng nghĩa nào không?
zionpi

7

Tôi biết tôi đến rất muộn bữa tiệc. Tôi đã gặp sự cố tương tự và vừa giải quyết nó như thế này:

public class TrackerRefResponse {

    private String applicationType;
    // Changed to Object. Works fine with String and array responses.
    private Object responseMessage;

}

Tôi thực sự chỉ thay đổi để gõ thành Đối tượng. Tôi chọn cách tiếp cận này vì chỉ có một trường trong phản hồi là động (đối với tôi, phản hồi của tôi phức tạp hơn nhiều), vì vậy việc sử dụng bộ chuyển đổi sẽ khiến cuộc sống trở nên khó khăn. Đã sử dụng Gson để làm việc với Đối tượng từ đó, tùy thuộc vào việc nó là giá trị Chuỗi hay Mảng.

Hy vọng điều này sẽ giúp ai đó đang tìm kiếm một câu trả lời đơn giản :).


3

Nếu không thể thay đổi API phụ trợ, tôi sẽ xem xét các biến thể sau (nếu Gson được sử dụng để chuyển đổi JSON).

  1. Chúng tôi có thể sử dụng bộ điều hợp loại Gson để tạo bộ điều hợp tùy chỉnh cho ResponseMessageloại quyết định động cách phân tích cú pháp JSON inoming (sử dụng một cái gì đó như if (reader.peek() == JsonToken.STRING)).

  2. Đặt một số thông tin meta mô tả loại phản hồi vào một tiêu đề HTTP và sử dụng nó để xác định loại thông tin nào phải được cung cấp cho cá thể Gson.



0

Tôi cũng chạy vấn đề này. nhưng tôi không chắc đây có phải trường hợp của bạn hay không, (tôi đang sử dụng Retrofit2)

trong trường hợp của tôi, tôi cần xử lý thông báo lỗi và thành công.

Thành công

{
"call_id": 1,
"status": "SUCCESS",
"status_code": "SUCCESS",
"result": {
    "data1": {
        "id": "RFP2UjW7p8ggpMXzYO9tRg==",
        "name": "abcdef",
        "mobile_no": "96655222",
        "email": ""
    },
    "data2": [
        {
            "no": "12345"
        },
        {
            "no": "45632"
        }
    ]
}
}

Có lỗi,

{
"call_id": 1,
"status": "FAILED",
"status_code": "NO_RECORDS",
"error": {
    "error_title": "xxx",
    "error_message": "details not found"
}
}

vì điều này, tôi vừa tạo một POJO khác 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;
}

trong trường hợp trên nếu resultkhóa trên json chứa data1, data2 thì khóa ValidateUserResult.javađược khởi tạo. nếu lỗi thì Error.javalớp được khởi tạo.


0

Tôi biết mình đến muộn, nhưng tôi chỉ muốn chia sẻ suy nghĩ của mình. Tôi đang làm việc trong một dự án mà tôi đang viết một phương pháp. Phương pháp sử dụng trang bị thêm để lấy dữ liệu từ máy chủ. Vì các nhà phát triển khác trong công ty của tôi sẽ sử dụng phương pháp này, tôi không thể sử dụng một POJOlớp (trong ví dụ của bạn là TrackerRefResponselớp). Vì vậy, tôi đã sử dụng JsonObject/Object như thế này:

interface APIService.java

public class APIService{
    @FormUrlEncoded
    @POST
    Call<JsonObject> myPostMethod(@Url String url, @Field("input") String input);
}

Sau đó, trong phương pháp của tôi, tôi đã viết thế này:

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
}

Bạn cũng có thể sử dụng Objectthay cho 'JsonObject`. Sau đó, khi bạn biết đó là loại phản hồi nào, có thể bạn có thể truyền nó thành đối tượng mong muốn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.