如何使用Retrofit从asynchronouscallback中返回String或JSONObject?

例如,打电话

api.getUserName(userId, new Callback<String>() {...}); 

原因:

 retrofit.RetrofitError: retrofit.converter.ConversionException: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a string but was BEGIN_OBJECT at line 1 column 2 

我想我必须禁用gsonparsing到POJO,但不知道如何做到这一点。

我想到了。 这很尴尬,但非常简单… 临时解决scheme可能是这样的:

  public void success(Response response, Response ignored) { TypedInput body = response.getBody(); try { BufferedReader reader = new BufferedReader(new InputStreamReader(body.in())); StringBuilder out = new StringBuilder(); String newLine = System.getProperty("line.separator"); String line; while ((line = reader.readLine()) != null) { out.append(line); out.append(newLine); } // Prints the correct String representation of body. System.out.println(out); } catch (IOException e) { e.printStackTrace(); } } 

但是,如果你想直接获取callback更好的方法是使用转换器 。

 public class Main { public interface ApiService { @GET("/api/") public void getJson(Callback<String> callback); } public static void main(String[] args) { RestAdapter restAdapter = new RestAdapter.Builder() .setClient(new MockClient()) .setConverter(new StringConverter()) .setEndpoint("http://www.example.com").build(); ApiService service = restAdapter.create(ApiService.class); service.getJson(new Callback<String>() { @Override public void success(String str, Response ignored) { // Prints the correct String representation of body. System.out.println(str); } @Override public void failure(RetrofitError retrofitError) { System.out.println("Failure, retrofitError" + retrofitError); } }); } static class StringConverter implements Converter { @Override public Object fromBody(TypedInput typedInput, Type type) throws ConversionException { String text = null; try { text = fromStream(typedInput.in()); } catch (IOException ignored) {/*NOP*/ } return text; } @Override public TypedOutput toBody(Object o) { return null; } public static String fromStream(InputStream in) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(in)); StringBuilder out = new StringBuilder(); String newLine = System.getProperty("line.separator"); String line; while ((line = reader.readLine()) != null) { out.append(line); out.append(newLine); } return out.toString(); } } public static class MockClient implements Client { @Override public Response execute(Request request) throws IOException { URI uri = URI.create(request.getUrl()); String responseString = ""; if (uri.getPath().equals("/api/")) { responseString = "{result:\"ok\"}"; } else { responseString = "{result:\"error\"}"; } return new Response(request.getUrl(), 200, "nothing", Collections.EMPTY_LIST, new TypedByteArray("application/json", responseString.getBytes())); } } } 

如果你知道如何改进这个代码 – 请随时写下来。

一个可能的解决scheme是使用JsonElement作为Callbacktypes( Callback<JsonElement> )。 在你原来的例子中:

 api.getUserName(userId, new Callback<JsonElement>() {...}); 

在成功的方法中,您可以将JsonElement转换为StringJsonObject

 JsonObject jsonObj = element.getAsJsonObject(); String strObj = element.toString(); 

Retrofit 2.0.0-beta3增加了一个converter-scalars模块,它提供了一个Converter.Factory用于将String ,8种基本types和8种盒装基元types转换为text/plain 。 在你的普通转换器之前安装这个,以避免通过这些简单的标量,例如,一个JSON转换器。

所以,首先添加converter-scalars build.gradle模块来为您的应用程序build.gradle文件。

 dependencies { ... // use your Retrofit version (requires at minimum 2.0.0-beta3) instead of 2.0.0 // also do not forget to add other Retrofit module you needed compile 'com.squareup.retrofit2:converter-scalars:2.0.0' } 

然后,像这样创build你的Retrofit实例:

 new Retrofit.Builder() .baseUrl(BASE_URL) // add the converter-scalars for coverting String .addConverterFactory(ScalarsConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build() .create(Service.class); 

现在你可以像这样使用API​​声明:

 interface Service { @GET("/users/{id}/name") Call<String> userName(@Path("userId") String userId); // RxJava version @GET("/users/{id}/name") Observable<String> userName(@Path("userId") String userId); } 

答案可能比已经提到的要短得多,不需要任何额外的库:

在声明中使用Response如下:

 ... Callback<Response> callback); 

并在处理响应时:

 @Override public void success(Response s, Response response) { new JSONObject(new String(((TypedByteArray) response.getBody()).getBytes())) } 

当@lordmegamax答案完全工作,有更好的解决scheme来自

Okio是一个新的库,它补充了java.io和java.nio

其他广场项目,已经紧张的retrofit ,因此你不需要添加任何新的依赖项,它必须是可靠的:

 ByteString.read(body.in(), (int) body.length()).utf8(); 

ByteString是一个不可变的字节序列。 对于字符数据,String是基础。 ByteString是String久违的兄弟,可以很容易地将二进制数据作为一个值来处理。 这个类是符合人体工程学的:它知道如何编码和解码自己的hex,base64和UTF-8。

完整的例子:

 public class StringConverter implements Converter { @Override public Object fromBody(TypedInput body, Type type) throws ConversionException { try { return ByteString.read(body.in(), (int) body.length()).utf8(); } catch (IOException e) { throw new ConversionException("Problem when convert string", e); } } @Override public TypedOutput toBody(Object object) { return new TypedString((String) object); } } 

这是我在debugging器中查到的。 注意:这是为了真正获取它在一个错误callback,而不是成功的callback。

你会看到成功的types可以通过调用retrofitError.getSuccessType()来find,并返回types为Type对象

然后你可以调用retrofitError.getBodyAs(YourType.class) ,这是我所需要做的,因为对我来说,它总是我期望的类。

这是一个简单的答案:

 retrofitError.getBodyAs(retrofitError.getSuccessType()) 

现在,我会注意到,我不必做任何这样的成功callback,因为它已经神奇地工作。

获取调用JSONObject或JSONArray

您可以创build自定义工厂或从这里复制它: https : //github.com/marcinOz/Retrofit2JSONConverterFactory