在对象类

时间:2017-03-16 17:12:31

标签: android reflection android-volley gson

我正在使用VolleyGson与Java Reflection反序列化我的JSON响应。我有一个特定的JSON字段,可以返回为JSONObject,这意味着一个对象类或JSONArray,这意味着一个对象类的数组/列表。

我需要反映这个字段并在运行时更改其类型。

以下是我尝试解析的JSON示例:

{
  "status message": "User Return Succsessfully",
  "status code": 200,
  "status Custom code": 0,
  "data": {
    "user_id": 5,
    "first_name": "Name1",
    "last_name": "Name2",
    "email": "me@mail.com",
  }
}

这个有数组:

{
  "status message": "User Return Succsessfully",
  "status code": 200,
  "status Custom code": 0,
  "data": [
    1,
    2,
    3
  ]
}

这是我的Object类:

public class BaseResponse implements Parsable, Serializable {

@SerializedName("status message")
@Expose
private String statusMessage;
@SerializedName("status code")
@Expose
private Integer statusCode;
@SerializedName("data")
@Expose
private Object object = null;

public String getStatusMessage() {
    return statusMessage;
}

public void setStatusMessage(String statusMessage) {
    this.statusMessage = statusMessage;
}

public Integer getStatusCode() {
    return statusCode;
}

public void setStatusCode(Integer statusCode) {
    this.statusCode = statusCode;
}

public Object getObject() {
    return object;
}

public void setObject(Object object) {
    this.object = object;
}

@Override
public Object parse(JsonElement jsonElement) {
    return new Gson().fromJson(jsonElement, BaseResponse.class);
}
}

"data"字段可能是JSONObject(需要将其解析为SomeObjectClass)或JSONArray(需要将其解析为SomeObjectClass2列表)

当我收到回复并以Gson格式返回时,我收到LinkedTreeMap,我无法将其解析为SomeObjectClassSomeObjectClass2

我需要反映"data"字段以根据响应获取任何类型的Object类。

我使用以下类返回响应:

public class GsonRequest<T> extends Request<T> {
private final Gson gson = new Gson();
private final Class<T> clazz;
private final Map<String, String> headers;
private final Response.Listener<T> listener;
private final Type type;

/**
 * Make a GET request and return a parsed object from JSON.
 *
 * @param url     URL of the request to make
 * @param clazz   Relevant class object, for Gson's reflection
 * @param headers Map of request headers
 */
public GsonRequest(int method, String url, Class<T> clazz, Type type, Map<String, String> headers,
                   Response.Listener<T> listener, Response.ErrorListener errorListener) {
    super(method, url, errorListener);
    this.clazz = clazz;
    this.type = type;
    this.headers = headers;
    this.listener = listener;
}

@Override
public Map<String, String> getHeaders() throws AuthFailureError {
    return headers != null ? headers : super.getHeaders();
}

@Override
protected void deliverResponse(T response) {
    listener.onResponse(response);
}

@Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
    try {
        String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
        return Response.success(gson.fromJson(json, clazz), HttpHeaderParser.parseCacheHeaders(response));
    } catch (UnsupportedEncodingException e) {
        return Response.error(new ParseError(e));
    } catch (JsonSyntaxException e) {
        return Response.error(new ParseError(e));
    }
}
}

如何实现我的目标?

2 个答案:

答案 0 :(得分:1)

你获得LinkedTreeMap个实例的原因是Gson没有足够的数据类型信息。您的BaseResponse只提供Object Gson没有类型信息提示,因此您必须在反序列化之前向Gson提供此类型信息。您可以拥有与此类似的BaseResponse类:

// Type parameterization <T> is used for conveniences at the use-site only
// Gson can't work with it without a type information hint anyway
// The Parsable interface seems to be unnecessary here -- parsing is a scope for Gson
final class BaseResponse<T>
        implements Serializable {

    @SerializedName("status message")
    @Expose
    final String statusMessage = null;

    @SerializedName("status code")
    @Expose
    final Integer statusCode = null;

    @SerializedName("data")
    @Expose
    final T data = null;

}

请注意,上面的类只代表一个响应,一个简单的数据映射,没有别的。如果你提供Gson类型的提示,用“vanilla”Gson解析你的JSON非常简单:

private static final String JSON_1 = "{\"status message\":\"User Return Succsessfully\",\"status code\":200,\"status Custom code\":0,\"data\":{\"user_id\":5,\"first_name\":\"Name1\",\"last_name\":\"Name2\",\"email\":\"me@mail.com\"}}";
private static final String JSON_2 = "{\"status message\":\"User Return Succsessfully\",\"status code\":200,\"status Custom code\":0,\"data\":[1,2,3]}";

// Java has java.lang.reflect.Type that provides more type information than a java.lang.Class does
// Why? The Class holds information about a concrete type, whilst Type can hold information about types that do not even exist in the application
// TypeToken instances declaration may look weird, but it's a nice and elegant way of specifying the type information via type parameters
private static final Type userBaseResponseType = new TypeToken<BaseResponse<User>>() {
}.getType();

private static final Type listOfIntegersBaseResponseType = new TypeToken<BaseResponse<List<Integer>>>() {
}.getType();

// Gson instances are known to be thread-safe so can be instantiated once and shared
// Instantiating a Gson instance is relatively an expensive operation, and just cache it
private static final Gson gson = new Gson();

public static void main(final String... args) {
    // Now just pass a Type instance to Gson
    // Note that the `fromJson(..., Type)` can "cast" itself, and passing just BaseResponse.class would work the same (not enough type information + unchecked warnings)
    final BaseResponse<User> userResponse = gson.fromJson(JSON_1, userBaseResponseType);
    final BaseResponse<List<Integer>> listOfIntegersResponse = gson.fromJson(JSON_2, listOfIntegersBaseResponseType);
    final User user = userResponse.data;
    System.out.println(user.firstName + " " + user.lastName + " (" + user.email + ")");
    System.out.println(listOfIntegersResponse.data);
}

User类的位置如下:

final class User {

    @SerializedName("user_id")
    @Expose
    final Integer userId = null;

    @SerializedName("first_name")
    @Expose
    final String firstName = null;

    @SerializedName("last_name")
    @Expose
    final String lastName = null;

    @SerializedName("email")
    @Expose
    final String email = null;

}

输出:

  

Name1 Name2(me@mail.com)
  [1,2,3]

现在,您可以从Class<T> clazz中移除GsonRequest,以便通过java.lang.reflect.Type正确提供类型信息(您的Type type代表什么?)或Gson TypeToken<T>方法调用的fromJson

旁注。您可以避免隐式克隆您传递给String构造函数的response.data数组。至于我看到Volley如何工作,你可以像这样包装它的data字段:

final Reader reader = new StringReader(new ByteArrayInputStream(response.data), HttpHeaderParser.parseCharset(response.headers));
... = gson.fromJson(reader, type), ...

这样可以为大回复节省一些内存。

答案 1 :(得分:0)

这样的事情:

public class BaseResponse<T> implements Parsable, Serializable {

    @SerializedName("status message")
    @Expose
    private String statusMessage;
    @SerializedName("status code")
    @Expose
    private Integer statusCode;
    @SerializedName("data")
    @Expose
    private T object = null;
    private Class<T> type;

    public BaseResponse(Class<T> zz) {
        type = zz;
    }

    public String getStatusMessage() {
        return statusMessage;
    }

    public void setStatusMessage(String statusMessage) {
        this.statusMessage = statusMessage;
    }

    public Integer getStatusCode() {
        return statusCode;
    }

    public void setStatusCode(Integer statusCode) {
        this.statusCode = statusCode;
    }

    public T getObject() {
        return object;
    }

    public void setObject(T object) {
        this.object = object;
    }

    @Override
    public T parse(JsonElement jsonElement) {
        return new Gson().fromJson(jsonElement, type);
    }
}

或者查看一下:http://www.artima.com/weblogs/viewpost.jsp?thread=208860

示例:

GsonRequest(method, url, SomeObjectClass.class, type, headers, listener, errorListener)

SomeObjectClass:

public class SomeObjectClass {
    public long user_id;
    public String first_name;
    public String last_name;
    public String email;
}