是否有可能實現以下兩個目標?使用Gson TypeAdapter以排序不敏感的方式反序列化JSON對象
- 能夠委託給調用我們的自定義實現的默認Gson解串器。
- 是由不同的順序按鍵在JSON對象
下面我介紹,只有實現這些目標的一個兩種可能的方法不受影響。
我現在不是一個成功的喜歡與回報工作的API:
{
"type": "success",
"data": {
"display_name": "Invisible Pink Unicorn",
"user_email": "[email protected]",
"user_id": 1234
}
}
或錯誤,如:
{
"type": "error",
"data": {
"error_name": "incorrect_password",
"error_message": "The username or password you entered is incorrect."
}
}
它目前的處理方式是通過註冊如果類型爲"error"
則拋出異常並帶有給定"error_message"
的TypeAdapter:
new GsonBuilder()
.registerTypeAdapter(User.class, new ContentDeserializer<User>())
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create()
public class ContentDeserializer<T> implements JsonDeserializer<T> {
@Override
public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
final JsonObject object = json.getAsJsonObject();
final String type = object.get("type").getAsString();
final JsonElement data = object.get("data");
final Gson gson = new Gson();
if ("error".equals(type)) {
throw gson.fromJson(data, ApiError.class);
} else {
return gson.fromJson(data, typeOfT);
}
}
}
它很整潔,因爲它非常簡潔,並且使用默認的解串器來完成所有的辛苦工作。
但實際上這是錯誤的,因爲它不使用相同的Gson委託該工作,所以它會使用不同的字段命名策略。
爲了解決這個問題,我寫了一個TypeAdapterFactory:
public class UserAdapterFactory implements TypeAdapterFactory {
@SuppressWarnings("unchecked")
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
if (!User.class.isAssignableFrom(type.getRawType())) return null;
final TypeAdapter<User> userAdapter = (TypeAdapter<User>) gson.getDelegateAdapter(this, type);
final TypeAdapter<ApiError> apiErrorAdapter = gson.getAdapter(ApiError.class);
return (TypeAdapter<T>) new Adapter(userAdapter, apiErrorAdapter);
}
private static class Adapter extends TypeAdapter<User> {
private final TypeAdapter<User> userAdapter;
private final TypeAdapter<ApiError> apiErrorAdapter;
Adapter(TypeAdapter<User> userAdapter, TypeAdapter<ApiError> apiErrorAdapter) {
this.userAdapter = userAdapter;
this.apiErrorAdapter = apiErrorAdapter;
}
@Override
public void write(JsonWriter out, User value) throws IOException {
}
@Override
public User read(JsonReader in) throws IOException {
User user = null;
String type = null;
in.beginObject();
while (in.hasNext()) {
switch (in.nextName()) {
case "type":
type = in.nextString();
break;
case "data":
if ("error".equals(type)) {
throw apiErrorAdapter.read(in);
} else if ("success".equals(type)) {
user = userAdapter.read(in);
}
break;
}
}
in.endObject();
return user;
}
}
}
這是一個很多工作,但至少讓我委託給同GSON配置。
這種方法的問題在於,它打破時,JSON對象將有不同的順序:
{
"data": {
"display_name": "Invisible Pink Unicorn",
"user_email": "[email protected]",
"user_id": 1234
},
"type": "success"
}
而且我沒有看到解決這個辦法,因爲我不認爲JsonReader
有一個選項可以讀取輸入兩次,也沒有辦法將「數據」值緩存在抽象類型中,例如JsonElement
以在遇到「類型」後解析。
爲什麼不反序列化'data'到'地圖'? –
syntagma
'context.deserialize(data,typeOfT);'不工作? –
@syntagma「user_id」不是一個字符串,對於初學者來說。但即使如果「type」是「錯誤」,那麼如何幫助我拋出'ApiError',否則返回'User'? – arekolek