具有Retrofit 2的多个转换器

我有一个HATEOAS(HAL) REST服务并设法使用下面的代码与它交谈(使用halarious作为转换引擎)但是当我尝试合并转换器 ( stallonestallone2 )时,应用程序将始终拿起第一个转换器而不是适合于响应类型的那个,当然会导致错误。

我怎样才能避免仅在小型细节中有所不同的重复改装?

 public interface Stallone { @GET("/discovery") Call discover(); @POST() Call login(@Url String url, @Body LoginRequest secret); } 
  public static void main(String... args) throws IOException { // Initialize a converter for each supported (return) type final Stallone stallone = new Retrofit.Builder() .baseUrl(BASE) .addConverterFactory(HALConverterFactory.create(DiscoveryResponse.class)) .build().create(Stallone.class); final Stallone stallone2 = new Retrofit.Builder() .baseUrl(BASE) .addConverterFactory(HALConverterFactory.create(LoginResponse.class)) .build().create(Stallone.class); // Follow the HAL links Response response = stallone.discover().execute(); System.out.println(response.code() + " " + response.message()); Assert.assertNotNull(response.body()); String loginPath = response.body().getLogin(); Assert.assertEquals(loginPath, "/login"); // Follow another link if (loginPath.startsWith("/")) loginPath = loginPath.substring(1); Response response2 = stallone2.login(loginPath, new LoginRequest(AUTH0TOKEN, null)).execute(); System.out.println(response2.code() + " " + response2.message()); Assert.assertNotNull(response2.body()); String setupPath = response2.body().getSetup(); Assert.assertEquals(setupPath, "/setup"); System.out.println("All OK!"); } 
 public final class HALConverterFactory extends Converter.Factory { private final Gson gson; public static HALConverterFactory create(Class type) { return new HALConverterFactory(type); } private HALConverterFactory(Class type) { if (!HalResource.class.isAssignableFrom(type)) throw new NullPointerException("Type should be a subclass of HalResource"); GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter(HalResource.class, new HalSerializer()); builder.registerTypeAdapter(HalResource.class, new HalDeserializer(type)); builder.setExclusionStrategies(new HalExclusionStrategy()); this.gson = builder.create(); } @Override public Converter fromResponseBody(Type type, Annotation[] annotations) { return new HALResponseBodyConverter(gson); } @Override public Converter toRequestBody(Type type, Annotation[] annotations) { return new GsonRequestBodyConverter(gson, type); } } 
 final class HALResponseBodyConverter implements Converter { private final Gson gson; HALResponseBodyConverter(Gson gson) { this.gson = gson; } @Override public T convert(ResponseBody value) throws IOException { BufferedSource source = value.source(); try { String s = source.readString(Charset.forName("UTF-8")); return (T) gson.fromJson(s, HalResource.class); } catch (Exception e) { throw new RuntimeException(e); } finally { closeQuietly(source); } } private static void closeQuietly(Closeable closeable) { if (closeable == null) return; try { closeable.close(); } catch (IOException ignored) { } } } 

同样,问题是,当你试图缩短上面这样:

  final Stallone stallone = new Retrofit.Builder() .baseUrl(BASE) .addConverterFactory(HALConverterFactory.create(DiscoveryResponse.class)) .addConverterFactory(HALConverterFactory.create(LoginResponse.class)) .build().create(Stallone.class); 

您将在Response response2 = ...行中获得exception:

线程“main”中的exceptionjava.lang.ClassCastException:com.example.retrofit.DiscoveryResponse无法强制转换为com.example.retrofit.LoginResponse

如果类型不匹配,您需要从Converter.Factory返回null 。 将Class保留在字段中以与其进行比较。

 @Override public Converter fromResponseBody(Type type, Annotation[] annotations) { if (!this.type.equals(type)) { return null; } return new HALResponseBodyConverter<>(gson); } 

这将允许使用多个实例,因为每个实例仅适用于其自己的类型。

也就是说,你可能只使用一个转换器并从传入的Type中拉出类来逃脱。

 @Override public Converter fromResponseBody(Type type, Annotation[] annotations) { if (!HALResponse.class.isAssignableFrom(type)) { return null; } // TODO create converter with `type` now that you know what it is... } 

您可以在repo中查看Wire转换器,以获取完整示例。

 package ch.halarious.core; import com.google.gson.JsonArray; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * Custom Hal Deserializer * * @author jaren */ public class CustomHalDeserializer extends HalDeserializer { /** * Intialisiert ein HalDeserializer-Objekt * * @param targetType Typ, den wir eigentlich deserialisieren sollten */ public CustomHalDeserializer(Class targetType) { super(targetType); } class CustomArrayList extends ArrayList implements HalResource{} public HalResource deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context, Class targetType) throws JsonParseException { // Es handelt sich um ein JSON-Objekt. JsonObject jsonObject = json.getAsJsonObject(); JsonObject embeddedRoot = jsonObject.getAsJsonObject(HalConstants.EMBEDDED_ROOT); if(embeddedRoot != null){ Set> set = embeddedRoot.entrySet(); if(set.toArray().length == 1){ JsonArray ja = embeddedRoot.getAsJsonArray(set.iterator().next().getKey()); if(ja.isJsonArray()) { CustomArrayList arrayResult = new CustomArrayList(); Iterator i = ja.iterator(); while(i.hasNext()){ JsonElement je = i.next(); arrayResult.add(super.deserialize(je, typeOfT, context, targetType)); } return arrayResult; } } } return super.deserialize(json, typeOfT, context, targetType); } } 

我做的几乎与@ jake-wharton在https://stackoverflow.com/a/33459073/2055854中说的相同,但添加了一些更改:

 public class GenericConverterFactory extends Converter.Factory { private final Class clazz; public static GenericConverterFactory create(Class clazz) { return new GenericConverterFactory(clazz); } private GenericConverterFactory(Class clazz) { this.clazz = clazz; } @Override public Converter responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { if (!isNeededType(type)) { return null; } // some converter that knows how to return your specific type T return new GenericConverter(clazz); } private boolean isNeededType(Type type) { if(type instanceof GenericArrayType) { // if type is array we should check if it has the same component as our factory clazz // if our factory clazz is not array getComponentType will return null return ((GenericArrayType) type).getGenericComponentType().equals(clazz.getComponentType()); } else if(clazz.getComponentType() == null) { // if factory clazz is not array and type is not array too // type is just a Class and we should check if they are equal return clazz.equals(type); } else { // otherwise our clazz is array and type is not return false; } } } 

类型来自改造界面,例如,如果您有:

 public interface SomeApi{ @GET("customelement") CustomElement[] getCustomElements(); @GET("customelement/{id}") CustomElement getCustomElement(@Path("id") int id); } 

对于方法, getCustomElements()类型将是GenericArrayTypeGenericComponentTypeCustomElement.class ,第二种方法类型为CustomElement.class

不确定它是否是最好的解决方案,但对我来说它是有效的。 希望能帮助到你。

在我的例子中,我需要将一个类序列化和反序列化为XML。 对于其他一切,我需要Json。 所以我注册了我的适配器:

 retrofit = new Retrofit.Builder() .baseUrl(BuildConfig.BASE_URL) .addConverterFactory(EditUserXmlConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create(createGson())) .client(httpClient.build()) .build(); 

因为我无法扩展SimpleXmlConverterFactory (不幸的是)我不得不使用我自己的类并更改以下行:

 if (!(type instanceof Class)) return null; 

 if (type != NeedToBeXML.class) return null; 

这样,只有NeedToBeXML类型的响应和请求NeedToBeXML转换为XML – 以及其他所有JSON。