たとえば、呼び出し
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
POJOへのgson解析を無効にする必要があると思いますが、その方法がわかりません。
私はそれを考え出した。恥ずかしいですが、とても簡単でした...一時的な解決策は次のようになります:
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();
}
}
しかし、直接コールバックを取得したい場合、より良い方法は 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()));
}
}
}
このコードを改善する方法を知っているなら、気軽にそれについて書いてください。
可能な解決策は、JsonElement
をCallback
タイプとして使用することです(Callback<JsonElement>
)。元の例:
api.getUserName(userId, new Callback<JsonElement>() {...});
Successメソッドでは、JsonElement
をString
またはJsonObject
に変換できます。
JsonObject jsonObj = element.getAsJsonObject();
String strObj = element.toString();
Retrofit 2.0.0-beta3は
converter-scalars
モジュールはConverter.Factory
は、String
、8つのプリミティブ型、および8つのボックス化されたプリミティブ型をtext/plain
ボディ。これらの単純なスカラーがJSONコンバーターなどを通過しないように、通常のコンバーターの前にこれをインストールします。
そのため、最初に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'
}
次に、次のように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の回答が完全に機能する場合、より優れた解決策があります。
Okioは、Java.ioおよびJava.nioを補完する新しいライブラリです。
retrofit
で既にタイトな他の正方形プロジェクト。したがって、新しい依存関係を追加する必要はなく、信頼性が必要です。
ByteString.read(body.in(), (int) body.length()).utf8();
ByteStringは、不変のバイトシーケンスです。文字データの場合、文字列が基本です。 ByteStringはStringの長い間失われた兄弟であり、バイナリデータを値として扱いやすくします。このクラスは人間工学に基づいています。16進数、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);
}
}