Serialize query parameter in retrofit

Submit the following request:

@POST("/recipes/create") void createRecipe(@Query("recipe") Recipe recipe, Callback<String> callback); 

I would like to have toJson (recipe), but unfortunately my request just calls toString () for my recipe, which doesn't work at all.

I could override toString inside the Recipe, but I would prefer a general solution.

I cannot use @Body as I need to specify what I am sending (I need to have "recipe = json (theRecipe)".

I also cannot change the serialization to add "recipe =" since I am not responsible for the server.

I am currently using the QueryMap map, where I put in a serialized object. Although this works, it is not a very good solution, in my opinion.

Is there any way to intercept the adapter adapter?

+5
source share
3 answers

I don’t think he supports it now, in any good way. Check this answer by one of the authors: https://github.com/square/retrofit/issues/291

The suggested method from this answer is to create a custom type that overrides the toString() method because Retrofit internally uses String.valueOf(value) to convert the query parameters or path to strings.

So you might 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; } } 
+6
source

Now this is possible when registering a custom Converter.Factory , which overrides the stringConverter method, which is called when parameters are resolved. the Github issue that @William mentioned 2 years ago does not seem to have been updated since the support was added.

Javadoc Method:

Returns the converter for converting the type to String, or null if the type cannot be processed by this factory. This is used to create converters for the types specified by @Field, @FieldMap, @Header, @HeaderMap, @Path, @Query and @QueryMap.

In the example below, the delegates are for Gson, but you can also apply any type of conversion 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; } } } } 

Converter registration:

To register a custom converter, your Retrofit constructor might look something like this:

  new Retrofit.Builder().baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create(gson)) .addConverterFactory(new GsonStringConverterFactory(gson)) .build(); 
+4
source

You can use retrofit.RestAdapter.Builder (). setConverter (...) for passing a custom json converter.

0
source

Source: https://habr.com/ru/post/1213364/


All Articles