У меня есть следующая иерархия классов
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... */
}
Мне нужно отправить экземпляр события в бэкэнд.
Случай 1. @Body
Когда я отправляю экземпляр события в тело запроса, он сериализуется в порядке.
Интерфейс Java RetroFit:
public interface EventAPI {
@POST("/event/create")
void sendEvent(@Body Event event, Callback<Long> cbEventId);
}
Журнал RetroFit:
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)
Случай 2. @Field
Но когда я отправляю экземпляр события в параметре запроса, сериализуется только абстрактный класс.
Интерфейс Java RetroFit:
@FormUrlEncoded
@POST("/event/create")
void sendEvent(@Field("event") Event event, Callback<Long> cbEventId);
Журнал RetroFit:
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)
Обратите внимание на разницу.
Вопросы
Почему?
Как отправить серийный экземпляр события на бэкэнд в параметре запроса?
Нужно ли писать собственный сериализатор JSON для абстрактного класса? (пример: Полиморфизм с JSON)
Или это специфичная функция RetroFit (чтобы игнорировать дочерние классы)?
Я также заметил, что во втором случае globalId
поле сериализованное имя globalId
, но оно должно быть id
! Это заставляет меня думать, что RetroFit использует другой GsonConverter
для @Field
, чем для параметров @Body
...
Конфигурация
Gradle зависимости
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>() {/* ... */});