Is there a way to turn a single String value (plain text, not json) into a JSON body with an 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;
}
Will give me what I expect:
{
"bar" : "hello world"
}
Is there a simple way to do it with an annotation? Something like this:
@POST("foo/{fooId}/bars")
Observable<Void> postBar(@Path("fooId") String styleId, @Body("bar") String bar);
Retrofit has a Converter.Factory
abstract class that you can use to do custom HTTP representation. You can create a Converter to construct a okhttp.RequestBody
if the method has a specific annotation.
The end result will look like:
@POST("/")
Call<Void> postBar(@Body @Root("bar") String foo)
and transform: postBar("Hello World")
into { "bar" : "Hello World" }
.
Let's get started.
Step 1 - create an annotation for the root key (Root.java)
/**
* Denotes the root key of a JSON request.
* <p>
* Simple Example:
* <pre><code>
* @POST("/")
* Call<ResponseBody> example(
* @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'm using Gson for JSON parsing 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 - install the JSONConverterFactory into your Retrofit 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);
It does not necessarily create a json body, but your api might be able to work with url encoded things
@FormUrlEncoded
@POST("foo/{fooId}/bars")
Observable<Void> postBar(@Path("fooId") String styleId, @Field("bar") String bar);
Then better to use Hashmap<String, String>
. You can pass Hashmap in body directly, by parsing using Gson.
And if you need to use multiple place then you can use your own map extending HashMap, so you will be able to add your values in one line. I am posting mine, might it 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 be like -
String postData = new PostParams().add("bar", "Hello World").toString()
Hope it will help :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With