Android 如何使用 Retrofit 从异步回调中返回 String 或 JSONObject?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21881943/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I return String or JSONObject from asynchronous callback using Retrofit?
提问by lordmegamax
For example, calling
例如,调用
api.getUserName(userId, new Callback<String>() {...});
cause:
原因:
retrofit.RetrofitError: retrofit.converter.ConversionException:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException:
Expected a string but was BEGIN_OBJECT at line 1 column 2
I think I must disable gson parsing into POJOs but can't figure out how to do it.
我想我必须禁用 gson 解析成 POJO,但不知道如何去做。
回答by lordmegamax
I figured it out. It's embarrassing but it was very simple... Temporary solutionmay be like this:
我想到了。这很尴尬,但很简单......临时解决方案可能是这样的:
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();
}
}
But if you want to get directly Callback the Better wayis to use Converter.
但是如果你想直接获得回调,更好的方法是使用Converter。
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()));
}
}
}
If you know how to improve this code - please feel free to write about it.
如果您知道如何改进此代码 - 请随时写下它。
回答by TPoschel
A possible solution would be to use JsonElement
as the Callback
type (Callback<JsonElement>
). In your original example:
一种可能的解决方案是使用JsonElement
asCallback
类型 ( Callback<JsonElement>
)。在您的原始示例中:
api.getUserName(userId, new Callback<JsonElement>() {...});
In the success method you can convert the JsonElement
to either a String
or a JsonObject
.
在成功方法中,您可以将 转换JsonElement
为 aString
或 a JsonObject
。
JsonObject jsonObj = element.getAsJsonObject();
String strObj = element.toString();
回答by floating cat
Retrofit 2.0.0-beta3 adds a
converter-scalars
module provides aConverter.Factory
for convertingString
, the 8 primitive types, and the 8 boxed primitive types astext/plain
bodies. Install this before your normal converter to avoid passing these simple scalars through, for example, a JSON converter.
Retrofit 2.0.0-beta3 添加了一个
converter-scalars
模块,提供了一个Converter.Factory
用于转换String
的 8 种原始类型和 8 种装箱原始类型作为text/plain
主体。在您的普通转换器之前安装它,以避免通过例如 JSON 转换器传递这些简单的标量。
So, first add converter-scalars
module to build.gradle
file for your application.
因此,首先将converter-scalars
模块添加到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'
}
Then, create your Retrofit
instance like this:
然后,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);
Now you can use API declaration like this:
现在您可以像这样使用 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);
}
回答by bgplaya
The answer may be much shorter than already mentioned and doesn't require any additional libraries:
答案可能比已经提到的要短得多,并且不需要任何额外的库:
In declaration use Response
as follows:
在声明中使用Response
如下:
... Callback<Response> callback);
And while handling response:
在处理响应时:
@Override
public void success(Response s, Response response) {
new JSONObject(new String(((TypedByteArray) response.getBody()).getBytes()))
}
回答by ar-g
When @lordmegamax answer completely work there is much nicer solution which is come from
当@lordmegamax 回答完全有效时,有更好的解决方案来自
Okio is a new library that complements java.io and java.nio
Okio 是一个补充 java.io 和 java.nio 的新库
other squares project which already tight with retrofit
and therefore you don't need to add any new dependency and it's have to be reliable:
其他 squares 项目已经很紧密了retrofit
,因此您不需要添加任何新的依赖项,并且它必须是可靠的:
ByteString.read(body.in(), (int) body.length()).utf8();
ByteString is an immutable sequence of bytes. For character data, String is fundamental. ByteString is String's long-lost brother, making it easy to treat binary data as a value. This class is ergonomic: it knows how to encode and decode itself as hex, base64, and UTF-8.
ByteString 是一个不可变的字节序列。对于字符数据,String 是基础。ByteString 是 String 失散已久的兄弟,可以轻松地将二进制数据视为值。这个类是符合人体工程学的:它知道如何将自己编码和解码为十六进制、base64 和 UTF-8。
Full example:
完整示例:
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);
}
}
回答by styler1972
Here is what I did, after poking around in the debugger. Note: this is for actually getting it inside an error callback, not the success callback.
这是我在调试器中闲逛后所做的。注意:这是为了在错误回调中实际获取它,而不是成功回调。
You'll see that the success type is found by calling retrofitError.getSuccessType()
and returns and object of type Type
你会看到成功类型是通过调用retrofitError.getSuccessType()
和返回类型的对象找到的Type
You can then call retrofitError.getBodyAs(YourType.class)
which is all I needed to do because for me its always the class I expect it to be.
然后你可以调用retrofitError.getBodyAs(YourType.class)
我需要做的所有事情,因为对我来说它总是我期望的类。
Here is the one-liner answer:
这是单行答案:
retrofitError.getBodyAs(retrofitError.getSuccessType())
Now, I'll note that I dont have to do anything like this regarding the success callback because it's already working magically.
现在,我会注意到我不必对成功回调做任何类似的事情,因为它已经神奇地工作了。
回答by oziem
To get Call JSONObject or JSONArray
获取调用 JSONObject 或 JSONArray
you can create custom factory or copy it from here : https://github.com/marcinOz/Retrofit2JSONConverterFactory
您可以创建自定义工厂或从这里复制它:https: //github.com/marcinOz/Retrofit2JSONConverterFactory