2017-04-07 28 views
1

我需要在請求參數之一中發送帶有自定義JSON對象的HTTP PUT請求。這是一個問題:當我使用Retrofit2時,我的序列化程序不會調用。Retrofit2:序列化程序不會被@呼叫@字段註釋參數

我的對象應該是這樣的:

{ 
    "ignore":["item1", "item2"] 
} 

當我把它直接效果很好:

final Gson gson = new GsonBuilder() 
     .registerTypeAdapter(MyModel.class, new MyModelSerializer()) 
     .create(); 
String s = gson.toJson(new MyModel(MyModel.ActionName.ACCEPT, new ArrayList<String>())); 
Log.d("tag", s); 

我得到{"accept":[]}。但是,當我與改造稱它爲我在日誌中看到這一點:D/OkHttp: name=abc&items=ru.bartwell.myapp.MyModel%4010a3d8b

我就與這個代碼的請求:)

@FormUrlEncoded 
@PUT("path/{id}") 
Call<ResultModel> getResult(@Path("id") long item, @Field("name") @NonNull String name, @Field("items") @NonNull MyModel myModel); 

getMyApi(方法:

try { 
    MyModel myModel = new MyModel(MyModel.ActionName.ACCEPT, new ArrayList<String>()); 
    Response<ResultModel> response = getMyApi().getResult(1, "abc", myModel).execute(); 
    if (response.isSuccessful()) { 
     ResultModel resultModel = response.body(); 
     // handle resultModel 
    } 
} catch (Exception e) { 
    e.printStackTrace(); 
} 

MyApi

public static MyApi getMyApi() { 
    final Gson gson = new GsonBuilder() 
     .registerTypeAdapter(MyModel.class, new MyModelSerializer()) 
     .create(); 

    return new Retrofit.Builder() 
      .baseUrl(BuildConfig.END_POINT_MY_API) 
      .client(MyOkHttpClient.create()) 
      .addConverterFactory(GsonConverterFactory.create(gson)) 
      .build() 
      .create(MyApi.class); 
} 

型號

public class MyModel { 
    @NonNull 
    private ActionName mActionName; 
    @NonNull 
    private List<String> mItems = new ArrayList<>(); 

    public MyModel(@NonNull final ActionName actionName, @NonNull final List<String> items) { 
     mActionName = actionName; 
     mItems = items; 
    } 

    @NonNull 
    public ActionName getActionName() { 
     return mActionName; 
    } 

    @NonNull 
    public List<String> getItems() { 
     return mItems; 
    } 

    public enum ActionName { 
     ACCEPT("accept"), DECLINE("decline"), IGNORE("ignore"); 

     private String mName; 

     ActionName(@NonNull final String name) { 
      mName = name; 
     } 

     public String getName() { 
      return mName; 
     } 
    } 
} 

串行

public class MyModelSerializer implements JsonSerializer<MyModel> { 
    @Override 
    public JsonElement serialize(@NonNull MyModel src, @NonNull Type typeOfSrc, @NonNull JsonSerializationContext context) { 
     JsonObject obj = new JsonObject(); 
     obj.add(src.getActionName().getName(), new Gson().toJsonTree(src.getItems())); 
     return obj; 
    } 
} 

如何解決呢?

回答

1

發生這種情況是因爲@Field -annated參數與stringConverter串行化。若要覆蓋默認行爲,你只需要添加另一個Converter.FactorystringConverter方法重寫:

.addConverterFactory(new Converter.Factory() { 
    @Override 
    public Converter<?, String> stringConverter(final Type type, final Annotation[] annotations, final Retrofit retrofit) { 
     // Example check for supported classes 
     if (type instanceof Class) { 
      final Class<?> clazz = (Class<?>) type; 
      if (MyModel.class.isAssignableFrom(clazz)) { 
       return (Converter<Object, String>) value -> gson.toJson(value, type); 
      } 
     } 
     return super.stringConverter(type, annotations, retrofit); 
    } 
}) 

那麼你的查詢參數字符串將是這樣的:

名= ABC &項目=%7B %22accept%22%3A%5B%5D%7D

(這是%7B%22accept%22%3A%5B%5D%7D' URI編碼{"accept":[]})。

幾個側面說明:

  • Gson實例是線程安全的,可以按整個應用程序創建一次(這是完全正常的單實例傳遞給所有組件)。
  • MyModelSerializer可以改進:你不應該有創建Gson實例,因爲解串器被綁定到Gson實例配置,所以你必須通過特定的上下文委派系列化像context.serialize(myModel.mItems, itemsType),其中itemsTypenew TypeToken<List<String>>() {}.getType()