如何在Retrofit中处理Dynamic JSON?

LOG*_*TAG 75 android json gson retrofit

我正在使用高效的网络库改造,但是我无法处理包含单个前缀的Dynamic JSON, responseMessage后者object随机responseMessage变化,在某些情况下(动态),相同的prefix()会更改为String.

Json格式responseMessage的对象:

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

}
Run Code Online (Sandbox Code Playgroud)

responseMessage Json格式动态更改为类型字符串:

 {
       "applicationType":"4",
       "responseMessage":"Success"          
 }
Run Code Online (Sandbox Code Playgroud)

对我来说问题是因为改造有内置的JSON解析我们必须为每个请求分配单个POJO!但遗憾的是,REST-API是使用动态JSON响应构建的,前缀将在成功(...)失败(...)方法中随机更改为字符串到对象!

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


            });
}
Run Code Online (Sandbox Code Playgroud)

POJO:

public class TrackerRefResponse {


private String applicationType;

    private String responseMessage;          //String type

//private ResponseMessage responseMessage;  //Object of type ResponseMessage

//Setters and Getters


}
Run Code Online (Sandbox Code Playgroud)

在上面的代码中POJO TrackerRefResponse.java前缀responseMessage设置为responseMessage类型的字符串或对象,因此我们可以创建具有相同名称的ref变量的POJO(java basics :))所以我JSON在Retrofit中寻找相同的动态解决方案.我知道在具有异步任务的普通http客户端中这是非常容易的工作,但它不是REST-Api JSON解析中的最佳实践!看看性能基准总是Volley或Retrofit是最好的选择,但我失败了处理动态JSON!

我知道可能的解决方案

  1. 使用旧的asyc任务与http客户端解析.:(

  2. 试着说服RESTapi后端开发人员.

  3. 创建自定义Retrofit客户端:)

Oli*_*ler 38

晚到派对,但你可以使用转换器.

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);
Run Code Online (Sandbox Code Playgroud)

然后你使用一个静态类来实现改造的转换器:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我编写了这个示例转换器,因此它以String,Object,JsonObject或Map <String,Object>的形式返回Json响应.显然并非所有的返回类型都适用于每个Json,并且肯定有改进的余地.但它演示了如何使用Converter将几乎任何响应转换为动态Json.

  • 看到`RestAdapter`这个例子是用于Retrofit 1.如何在Retrofit 2中实现相同的转换器? (11认同)
  • ConversionException 在 Retrofit 2 中不可用:( (2认同)

Yuk*_*ida 20

RestClient.java

import retrofit.client.Response;
public interface RestClient {
  @GET("/api/foo") Response getYourJson();
}
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

您必须实现"checkResponseMessage"方法.


Nav*_*hna 15

使用gson-converter以下方法尝试自定义反序列化(Retrofit 2.0的更新答案)

创建三个模型,如下所示

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

}
Run Code Online (Sandbox Code Playgroud)

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

}
Run Code Online (Sandbox Code Playgroud)

ResponseString

public class ResponseString extends ResponseWrapper {

}
Run Code Online (Sandbox Code Playgroud)

UserResponseDeserializer(自定义反序列化器)

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

}
}
Run Code Online (Sandbox Code Playgroud)

改造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) {
        }
    });
Run Code Online (Sandbox Code Playgroud)

使用的库

编译'com.squareup.retrofit2:改造:2.3.0'

编译'com.squareup.retrofit2:converter-gson:2.3.0'

***** 上一个答案(以上答案更推荐一个)*****

像这样改变你的pojo

public class TrackerRefResponse {

  private String applicationType;
  private Object responseMessage;

  public Object getResponseMessage() {
  return responseMessage;
  }

  public void setResponseMessage(Object responseMessage) {
  this.responseMessage = responseMessage;
 }
}
Run Code Online (Sandbox Code Playgroud)

并像这样改变改造的onResponse

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

您还可以查看此帖子,了解有关动态json解析的更多详细信息


Jus*_*ade 10

任何可能的解决方案都可行.你还可以做的是将Retrofit api接口返回类型发送到响应.通过该响应,您将获得一个Inputstream可以转换为JSON对象并按您认为合适的方式读取的主体.

请看:http://square.github.io/retrofit/#api-declaration - 在RESPONSE OBJECT TYPE下

更新

Retrofit 2现已推出,并对文档和库进行了一些更改.

查看http://square.github.io/retrofit/#restadapter-configuration,可以使用请求和响应正文对象.

  • 我恐怕找不到您提供的部分,有没有同义词? (6认同)

med*_*eda 9

接受的答案对我来说似乎过于复杂,我这样解决:

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());
    }
});
Run Code Online (Sandbox Code Playgroud)

这只是一个示例,试图向您展示如何使用不同的模型.

变量isEmail只是您使用适当模型的条件的布尔值.

  • 尽管如此,如果您在进行呼叫之前不知道响应类型,我认为这无济于事,这就是问题中的情况.当然你可以先得到Response Body的InputStream,读几行,确定Body的Type,然后转换它.但它并不那么简单. (2认同)
  • 我认为它不会起作用,因为Deserializer会抛出异常并到达onFailure()而不是onResponse() (2认同)

Lun*_*box 7

我知道我参加聚会非常晚了.我遇到了类似的问题,就这样解决了:

public class TrackerRefResponse {

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

}
Run Code Online (Sandbox Code Playgroud)

我只是改为键入到Object.我选择这种方法是因为响应中只有一个字段是动态的(对我而言,我的响应更复杂),因此使用转换器会使生活变得困难.使用Gson从那里使用Object,具体取决于它是String还是Array值.

希望这有助于寻找简单答案的人:).