JSON Body formatting for retrofitting from a single-line value without a model

Is there a way to turn a single String value (plain text, not json) into a JSON body with annotation? I do not want to create such a simple model.

Example

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

Give me what I expect:

 { "bar" : "hello world" } 

Is there an easy way to do this with annotation? Something like that:

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

Retrofit has an abstract Converter.Factory class that you can use to create a custom HTTP view. You can create a converter to build okhttp.RequestBody if the method has a specific annotation.

The end result will look like this:

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

and converting: postBar("Hello World") to { "bar" : "Hello World" } .

Let's start.

Step 1 - create an annotation for the root key (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(); } 

Step 2 - Define your Converter.Factory that detects the annotation (JSONConverterFactory.java). I use Gson to parse JSON, but you can use whatever framework you want.

 /** * 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)); } } } 

Step 3 - set the JSONConverterFactory to the RetoFit instance

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

Step 4 - Profit

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

Or for your case:

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

Then it is better to use Hashmap<String, String> . You can pass the Hashmap directly in the body using parsing with Gson. And if you need to use multiple places, you can use your own map extending the HashMap so that you can add your values ​​in one line. I post mine, maybe this will help you -

 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); } } 

Usage will look like

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

Hope this helps :)

+2
source

This does not necessarily create a json body, but your api can work with url encoded things.

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

I use like this:

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

It will change the map to JsonObject

0
source

All Articles