2016-07-06 2 views
5

Есть ли способ превратить одно значение String (обычный текст, а не json) в тело JSON с аннотацией? Я не хочу создавать такую ​​простую модель.Формат JSON Body для дооснащения от однострочного значения без модели

Пример

@POST("foo/{fooId}/bars") 
Observable<Void> postBar(@Path("fooId") String styleId, @Body BarModel bar); 

class BarModel { 
    public String bar; 
} 

Даст мне то, что я ожидал:

{ 
    "bar" : "hello world" 
} 

Есть простой способ сделать это с аннотациями? Что-то вроде этого:

@POST("foo/{fooId}/bars") 
Observable<Void> postBar(@Path("fooId") String styleId, @Body("bar") String bar); 
+0

Смотрите также это [мета-обсуждение] (http://meta.stackoverflow.com/q/327487/230513). – trashgod

ответ

7

дооснащения имеет Converter.Factory абстрактный класс, который можно использовать для выполнять пользовательское представление HTTP. Вы можете создать конвертер для построения okhttp.RequestBody, если метод имеет конкретную аннотацию.

Конечный результат будет выглядеть следующим образом:

@POST("/") 
Call<Void> postBar(@Body @Root("bar") String foo) 

и преобразование: postBar("Hello World") в { "bar" : "Hello World" }.

Давайте начнем.

Шаг 1 - создать аннотацию для корневого ключа (Root.java)

/** 
* Denotes the root key of a JSON request. 
* <p> 
* Simple Example: 
* <pre><code> 
* &#64;POST("/") 
* Call&lt;ResponseBody&gt; example(
*  &#64;Root("name") String yourName); 
* </code></pre> 
* Calling with {@code foo.example("Bob")} yields a request body of 
* <code>{name=>"Bob"}</code>. 
* @see JSONConverterFactory 
*/ 
@Documented 
@Target(PARAMETER) 
@Retention(RUNTIME) 
public @interface Root { 
    /** 
    * The value of the JSON root. 
    * Results in {"value" : object} 
    */ 
    String value(); 
} 

Шаг 2 - определите Converter.Factory, который определяет аннотацию (JSONConverterFactory.java). Я использую Gson для разбора JSON, но вы можете использовать любую инфраструктуру, которую хотите.

/** 
* Converts @Root("key") Value to {"key":json value} using the provided Gson converter. 
*/ 
class JSONConverterFactory extends Converter.Factory { 
    private final Gson gson; 
    private static final MediaType CONTENT_TYPE = 
      MediaType.parse("application/json"); 

    JSONConverterFactory(Gson gson) { 
     this.gson = gson; 
    } 

    @Override 
    public Converter<?, RequestBody> requestBodyConverter(
      Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) { 
     for (Annotation annotation : parameterAnnotations) { 
      if (annotation instanceof Root) { 
       Root rootAnnotation = (Root) annotation; 
       return new JSONRootConverter<>(gson, rootAnnotation.value()); 
      } 
     } 
     return null; 
    } 

    private final class JSONRootConverter<T> implements Converter<T, RequestBody> { 
     private Gson gson; 
     private String rootKey; 

     private JSONRootConverter(Gson gson, String rootKey) { 
      this.gson = gson; 
      this.rootKey = rootKey; 
     } 

     @Override 
     public RequestBody convert(T value) throws IOException { 
      JsonElement element = gson.toJsonTree(value); 
      JsonObject object = new JsonObject(); 
      object.add(this.rootKey, element); 
      return RequestBody.create(CONTENT_TYPE, this.gson.toJson(object)); 
     } 
    } 
} 

Шаг 3 - установить JSONConverterFactory в свой Retrofit например

Gson gson = new GsonBuilder().create(); // Or your customized version 
Retrofit.Builder builder = ...; 
builder.addConverterFactory(new JSONConverterFactory(gson)) 

Шаг 4 - Прибыль

@POST("/") 
Call<Void> postBar(@Body @Root("bar") String foo) 

Или для Вашего случая:

@POST("foo/{fooId}/bars") 
Observable<Void> postBar(@Body @Root("bar") String barValue, @Path("fooId") String styleId); 
+0

«Core» retrofit - это http-клиент (и только то, что «$». Я спрашивал их о том, как лучше поддерживать JSON в основной библиотеке, и им это неинтересно. Retrofit был явно разработан для расширения, поэтому это «правильный» подход в соответствии с дизайнерами – Kevin

+0

@ Ник, есть ли что-нибудь еще, что сделало бы этот ответ лучше/по какой-либо причине, по которой вы не хотите его принять? – Kevin

+0

Я надеялся, что будет однострочное решение для моей проблемы, это много, но я соглашусь с тем, что это может помочь другим –

0

Это не обязательно создает JSon тело, но ваш апи может быть в состоянии работать с кодировке URL вещи

@FormUrlEncoded 
@POST("foo/{fooId}/bars") 
Observable<Void> postBar(@Path("fooId") String styleId, @Field("bar") String bar); 
+0

Спасибо, не пробовал. Это не API, над которым я контролирую, так что ожидаю, что тело json понравится тому, которое я написал. (Это дало мне bar = hello% 20world) –

+0

вы все еще можете попробовать это, это можно использовать вместо тела довольно часто. –

2

Тогда лучше использовать Hashmap<String, String>. Вы можете передать Hashmap непосредственно в теле, используя парсинг с помощью Gson. И если вам нужно использовать несколько мест, вы можете использовать свою собственную карту, расширяющую HashMap, так что вы сможете добавить свои значения в одну строку. Я отправляю мой, может это помочь вам -

public class PostParams extends HashMap<String, String> { 
    public static PostParams init() { 
     return new PostParams(); 
    } 

    public PostParams add(String param, String value) { 
     put(param, value); 
     return this; 
    } 

    public PostParams add(String param, String[] values) { 
     put(param, new Gson().toJson(values)); 
     return this; 
    } 

    public PostParams add(String param, int[] values) { 
     put(param, new Gson().toJson(values)); 
     return this; 
    } 

    public PostParams add(String param, int value) { 
     put(param, value + ""); 
     return this; 
    } 

    public PostParams addPlatform() { 
     put("Platform", Constants.ANDROID); 
     return this; 
    } 

    public PostParams add(String param, double value) { 
     put(param, new Gson().toJson(value)); 
     return this; 
    } 

    @Override 
    public String toString() { 
     return new Gson().toJson(this); 
    } 
} 

Использования будет как -

String postData = new PostParams().add("bar", "Hello World").toString() 

Надеется, что это поможет :)

0

я использую как это:

@POST("url") 
Observable<Response<Object>> login(@Body Map<String, Object> body); 

Это изменит карту в JSONObject

Смежные вопросы