Android - GSon + RetroFit 中的继承和抽象类

标签 android gson abstract-class retrofit

我有以下类层次结构

public abstract class SyncModel {
    @Expose
    @SerializedName("id")
    private Long globalId;

    @Expose
    protected DateTime lastModified;
    
    /* Constructor, methods... */
}

public class Event extends SyncModel {
    @Expose
    private String title;
    
    /* Other fields, constructor, methods... */
}

我需要向后端发送一个事件实例。

案例一。@Body

当我在请求正文中发布事件实例时,它被很好地序列化了。

RetroFit Java 接口(interface):

public interface EventAPI {
    @POST("/event/create")
    void sendEvent(@Body Event event, Callback<Long> cbEventId);
}

改造日志:

D   Retrofit    ---> HTTP POST http://hostname:8080/event/create
D   Retrofit    Content-Type: application/json; charset=UTF-8
D   Retrofit    Content-Length: 297
D   Retrofit    {"title":"Test Event 01",...,"id":null,"lastModified":"2015-07-09T14:17:08.860+03:00"}
D   Retrofit    ---> END HTTP (297-byte body)

Case 2. @Field

But when I post the Event instance in a request parameter, only abstract class is serialized.

RetroFit Java interface:

@FormUrlEncoded
@POST("/event/create")
void sendEvent(@Field("event") Event event, Callback<Long> cbEventId);

改造日志:

D   Retrofit    ---> HTTP POST http://hostname:8080/event/create
D   Retrofit    Content-Type: application/x-www-form-urlencoded; charset=UTF-8
D   Retrofit    Content-Length: 101
D   Retrofit    event=SyncModel%28globalId%3Dnull%2C+lastModified%3D2015-07-09T13%3A36%3A33.510%2B03%3A00%29
D   Retrofit    ---> END HTTP (101-byte body)

Notice the difference.

Questions

Why?
How can I send a serialized Event instance to backend in a request parameter?
Do I need to write a custom JSON serializer for abstract class? (example: Polymorphism with JSON)
Or is it a RetroFit specific feature (to ignore child classes)?

I've also noticed that in the 2nd case globalId field serialized name is globalId, but it should be id! It makes me think that RetroFit uses a different GsonConverter for @Field than for @Body parameters...


Configuration

Gradle dependencies

compile 'com.squareup.retrofit:retrofit:1.9.+'
compile 'com.squareup.okhttp:okhttp:2.3.+'
compile 'net.danlew:android.joda:2.8.+'
compile ('com.fatboyindustrial.gson-jodatime-serialisers:gson-jodatime-serialisers:1.1.0') {  // GSON + Joda DateTime
    exclude group: 'joda-time', module: 'joda-time'
}

REST 客户端

public final class RESTClient {

    // Not a real server URL
    public static final String SERVER_URL = "http://hostname:8080";

    // one-time initialization
    private static GsonBuilder builder = new GsonBuilder()
            .serializeNulls()
            .excludeFieldsWithoutExposeAnnotation()
            .setDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'");
    // Joda DateTime type support
    private static Gson gson = Converters.registerDateTime(builder).create();

    private static RestAdapter restAdapter = new RestAdapter.Builder()
            .setLogLevel(RestAdapter.LogLevel.FULL)     // for development
            .setEndpoint(SERVER_URL)
            .setConverter(new GsonConverter(gson))    // custom converter
            .build();

    private static final EventAPI eventService = restAdapter.create(EventAPI.class);
    /* + Getter for eventService */

    static {
        // forget them
        restAdapter = null;
        gson = null;
        builder = null;
    }

}

打电话

RESTClient.getEventService().sendEvent(event, new Callback<Long>() {/* ... */});

最佳答案

看看@Field's documentation .它说:

Values are converted to strings using String#valueOf(Object) and then form URL encoded.

String#valueOf(Object) 在内部调用 Object#toString()。我想您的 SyncModel 有一个 toString() 方法而 Event 没有。当 Retrofit 调用 String.valueOf(event) 时,将调用 SyncModel#toString() 而不是 Event#toString()。这就是为什么您在 Retrofit 日志中看不到 title 的原因。

Gson 在转换@Field 参数时根本不起任何作用。可以——你可以让你的 toString() 方法看起来像这样:

@Override
public String toString() {
    return GsonProvider.getInstance().toJson(this);
}

将其放入您的抽象 SyncModel 类中,它也应该适用于 Event

关于Android - GSon + RetroFit 中的继承和抽象类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31317291/

相关文章:

java - 如何解码android.os.Build.SERIAL?

android - 从应用程序 Android 注销

android - 检测 fragment 主视图的测量传递何时完成(或 Android 等效的 viewDidLayoutSubviews)

java - 有人可以澄清 Gson 的 unicode 编码吗?

java - 尝试获取 Json 中某个元素内的元素或其本身的值返回 null

android - 在 retrofit 响应中接收空体

java - 类实例化 - 变量的抽象类或具体类

使用抽象接口(interface)的 C++ 编译器独立 DLL

c++ - 我如何在其他类(或任何类)中使用抽象类?

android - 用 AppCompat 替换 ActionBarSherlock