Imagine the following request:
@POST(\"/recipes/create\")
void createRecipe(@Query(\"recipe\") Recipe recipe, Callback callback);
As @Rolf mentioned, there is a way to set customConverter.Factory
Example:
public class QueryConverterFactory extends Converter.Factory {
public static QueryConverterFactory create() {
return new QueryConverterFactory();
}
private QueryConverterFactory() {
}
@Nullable
@Override
public Converter<?, String> stringConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
if (type == Date.class) {
return DateQueryConverter.INSTANCE;
}
return null;
}
private static final class DateQueryConverter implements Converter<Date, String> {
static final DateQueryConverter INSTANCE = new DateQueryConverter();
private static final ThreadLocal<DateFormat> DF = new ThreadLocal<DateFormat>() {
@Override
public DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
@Override
public String convert(Date date) {
return DF.get().format(date);
}
}
}
You can add converters for your own types.
I don't think it supports this right now in some nice way. Check this answer by one of the authors: https://github.com/square/retrofit/issues/291
The suggested method from that answer is to create a custom type that overrides the toString()
method, because Retrofit internally uses String.valueOf(value)
to convert query or path parameters to strings.
So, you could have something like this:
class Recipe {
public int id;
public String title;
@Override
public String toString() {
// You can use a GSON serialization here if you want
// This is not a robust implementation
return Integer.toString(id) + "-" + title;
}
}
You can use retrofit.RestAdapter.Builder().setConverter(...) to pass a custom json converter.
This is now possible when registering a custom Converter.Factory
that overrides the stringConverter
method, which is called when resolving parameters. The Github issue that @William referred to 2 years ago doesn't seem to be updated since support was added.
Method Javadoc:
Returns a Converter for converting type to a String, or null if type cannot be handled by this factory. This is used to create converters for types specified by @Field, @FieldMap values, @Header, @HeaderMap, @Path, @Query, and @QueryMap values.
The example below delegates to Gson, but in the same way any type of conversion can be applied to the parameters.
Example: GsonStringConverterFactory
class GsonStringConverterFactory
extends Converter.Factory {
private final transient Gson gson;
GsonStringConverterFactory(final Gson gson) {
this.gson = gson;
}
@Override
public Converter<?, String> stringConverter(final Type type, final Annotation[] annotations, final Retrofit retrofit) {
final TypeAdapter typeAdapter;
typeAdapter = gson.getAdapter(TypeToken.get(type));
return new StringConverter<>(typeAdapter);
}
private static class StringConverter<T>
implements Converter<T, String> {
private final TypeAdapter<T> typeAdapter;
private StringConverter(final TypeAdapter<T> typeAdapter) {
this.typeAdapter = typeAdapter;
}
@Override
public String convert(final T value)
throws IOException {
/* This works in our case because parameters in this REST api are always some kind of scalar
* and the toJson method converts these to simple json types. */
final String jsonValue;
jsonValue = typeAdapter.toJson(value));
if (jsonValue.startsWith("\"") && jsonValue.endsWith("\"") {
/* Strip enclosing quotes for json String types */
return jsonValue.substring(1, jsonValue.length() - 1);
} else {
return jsonValue;
}
}
}
}
Registering the converter:
To register the custom converter, your Retrofit builder could look something like this:
new Retrofit.Builder().baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.addConverterFactory(new GsonStringConverterFactory(gson))
.build();