ClassCastException 对象继承与改造

ClassCastException Object Inheritance with Retrofit

我正在为我们的核心网络从 Asynctask + Loader 迁移到 Retrofit。一切顺利,直到我遇到了以前没有发生过的 ClassCastExceptions:

ImagePojo imagePojo = (ImagePojo) mediaPojo; // Error: cannot cast MediaPojo to ImagePojo

这是层次结构中我的 POJO 的骨架:

Parent/Generic 媒体对象

public class MediaPojo extends CustomObservable implements Parcelable {
    @SerializedName("media_id")
    private String mMid;
    @SerializedName("type")
    private String mType;
    @SerializedName("title")
    public MediaPojo() {
    }

    @Override
    public int describeContents() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(mNid);
        dest.writeString(mType);
        dest.writeString(mTitle);
    }

    public static final Parcelable.Creator<MediaPojo> CREATOR = new Parcelable.Creator<MediaPojo>() {
        @Override
        public MediaPojo createFromParcel(Parcel source) {
            return new MediaPojo(source);
        }

        @Override
        public MediaPojo[] newArray(int size) {
            return new MediaPojo[size];
        }
    };
...
} 

// 定义所有媒体类型通用的字段

视频媒体类型

public class VideoPojo extends MediaPojo implements Parcelable {
    @SerializedName("video_id")
    private String mVid;
    @SerializedName("video_url")
    private String mVideoUrl;
    @SerializedName("thumbnail")
    private String mThumbnail ...
}

图像媒体类型 POJO

public class ImagePojo extends MediaPojo implements Parcelable {
    @SerializedName("image_url")
    private String mImageUrl;
    @SerializedName("width")
    private String mImageWidth;
    @SerializedName("height")
    private String mImageHeight
    ...
}

// 其他类型:TextPojo、InfoGraphicPojo ...

到目前为止,我在迁移中对 POJO 所做的唯一更改是添加 @SerializedName 注释以允许 Retrofit 自动将 JSON 字段映射到定义的 POJO 实例变量。我发现有必要在运行时将通用 MediaPojo 转换为特定类型的 POJO,因为我对 api.mydomain.com/media 的 REST 调用可以 return JSON 表示各种类型的媒体(视频、图片等)。

有没有办法让 Retrofit 绕过我已经存在的对象继承结构?或者它可以像我使用 Parcelable 接口进行序列化的方式那样吗?

是的,问题是 gson 总是在改造调用中创建类型。您需要提供一个 custom deserializer 来根据您的 type 字段找出对象类型。这是可能看起来像的草图。

public class ModiaPojoDeserializer implements JsonDeserializer<Model> {

    private static final String TYPE_FIELD = "type";
    private static final String IMAGE_TYPE = "image";
    private static final String VIDEO_TYPE = "video";

    @Override
    public Model deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        if (json.isJsonObject() && json.getAsJsonObject().has(TYPE_FIELD)) {
            JsonObject jsonObject = json.getAsJsonObject();
            final String type = jsonObject.get(TYPE_FIELD).getAsString();
            if (VIDEO_TYPE.equals(type)) {
                return context.deserialize(json, VideoPojo.class);
            } else if (IMAGE_TYPE.equals(type)) {
                return context.deserialize(json, ImagePojo.class);
            } // ...
            // If you need to deserialize as MediaPojo,
            // deserialize without the current context 
            // or you will infinite loop
            return new Gson().fromJson(json, typeOfT);

        } else {
            // Return a blank object on error, could use null
            return new MediaPojo();
        }
    }
}

然后您将使用 Gson.Builder 使用您的自定义反序列化器创建一个自定义 gson 对象 -

 Gson gson = new GsonBuilder()
     .registerTypeAdapter(MediaPojo.class, new MediaPojoDeserializer())
     .create();

然后在创建 RestAdapter 时添加它 --

RestAdapter restAdapter = new RestAdapter.Builder()
    .setEndpoint("https://api.mydomain.com")
    .setConverter(new GsonConverter(gson))
    .build();