如何在翻新中处理动态JSON?


82

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

Json格式的responseMessage对象:

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

}

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

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

我的问题是,因为改造具有内置的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();
                }


            });
}

Pojo:

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被设置为string或类型为responseMessage的对象,因此我们可以使用具有相同名称的ref变量创建POJO(java basics :)),因此我正在为JSONRetrofit中的动态寻找相同的解决方案。我知道这在具有异步任务的普通http客户端中是非常容易的工作,但这不是REST-ApiJSON解析中的最佳实践!始终看性能基准排球或翻新是最好的选择,但是我无法动态处理JSON

我知道可能的解决方案

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

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

  3. 创建自定义的改造客户端:)


1
“尝试说服RESTapi后端开发人员。” 为我做了把戏!大声笑!;)(nb:我也是后端开发人员,我也要说服自己!)
mrx

Answers:


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

然后,您使用一个静态类来实现改装的Converter:

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

我已经编写了此样本转换器,因此它以字符串,对象,JsonObject或Map <字符串,对象>的形式返回Json响应。显然,并非所有返回类型都适用于每个Json,并且肯定有改进的空间。但是它演示了如何使用Converter将几乎所有响应转换为动态Json。


13
看到RestAdapter此示例适用于翻新1。如何在翻新2中实现相同的转换器?
androidtitan

1
ConversionException在Retrofit 2中不可用:(
mutkan '16

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

您必须实现“ checkResponseMessage”方法。


在Retrofit 2中该如何做?
Vadim Kotov

什么是“ checkResponseMessage”?
Shashank singh,

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

}

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(自定义反序列化器)

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

}
}

改造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'

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

像这样改变你的pojo

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解析的更多详细信息


ResponseWrapper类真的必要吗?我认为这看起来很混乱。我需要除层次结构中最高的对象之外的任何对象上的转换器...
RabbitBones22 '19

1
您可避免包装类如果它的混乱,试试这个freshbytelabs.com/2018/12/...
那伏乃尔克里希纳

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

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

该变量isEmail只是一个布尔值,供您使用适当模型的条件使用。


您能详细说明一下吗?此代码是非描述性的。mType来自哪里?
Desdroid '16

@Desdroid我简化了代码,然后进行了解释扩展
meda

2
不过,我相信如果您在致电之前不知道响应类型,这将无济于事,这就是问题所在。当然,您可以先获取响应主体的InputStream,阅读几行,确定主体是哪种类型,然后对其进行转换。但这不是那么简单。
Desdroid '16

我正在寻找处理不同退货类型的最佳方法。您的答案看起来很有希望,但我不确定您从哪里知道Type。这就是为什么我要您详细说明;)
Desdroid

2
我认为它将无法正常工作,因为Deserializer会引发异常并到达onFailure()而非onResponse()
Amt87 '16

9

您任何可能的解决方案都将起作用。您还可以做的是将Retrofit api接口返回类型发送给响应。通过该响应,您可以获得一个正文Inputstream,可以将其转换为JSON对象并按自己的意愿阅读。

查看:http : //square.github.io/retrofit/#api-declaration-在“响应对象类型”下

更新

Retrofit 2现已发布,并对文档和库进行了一些更改。

查看http://square.github.io/retrofit/#restadapter-configuration,其中有可以使用的请求和响应主体对象。


6
恐怕找不到您提供的部分,是否有任何同义词?
zionpi

7

我知道我晚会很晚。我有一个类似的问题,只是这样解决了:

public class TrackerRefResponse {

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

}

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

希望这可以帮助寻找简单答案的人:)。


3

如果无法更改后端API,我将考虑以下变体(如果使用Gson转换JSON)。

  1. 我们可以使用Gson类型适配器为该ResponseMessage类型创建自定义适配器,该类型可以动态决定如何解析无误JSON(使用if (reader.peek() == JsonToken.STRING))。

  2. 将描述响应类型的一些元信息放入HTTP标头中,并使用它来确定必须将哪些类型信息馈送到Gson实例。



0

我也遇到这个问题。但是我不确定这是否是您的情况,(我正在使用Retrofit2)

就我而言,我需要处理错误和成功消息。

成功

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

发生错误时,

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

为此,我刚刚创建了另一个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;
}

在上述情况下,如果resultjson上的键包含data1,data2,则将ValidateUserResult.java初始化get。如果错误,则Error.java初始化该类。


0

我知道我迟到了,但我只想分享自己的想法。我在一个正在编写方法的项目中工作。该方法使用改造从服务器获取数据。由于公司中的其他开发人员将使用此方法,因此无法使用POJO类(在您的示例中为TrackerRefResponse类)。所以我用JsonObject/Object像这样:

接口APIService.java

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

然后在我的方法中,我这样写:

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
}

您也可以使用Object代替'JsonObject`。稍后,当您知道响应的类型时,也许可以将其转换为所需的对象。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.