如何使用Retrofit从异步回调中返回String或JSONObject?

时间:2014-02-19 13:29:13

标签: android retrofit retrofit2

例如,调用

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

我想我必须禁用gson解析到POJO,但无法弄清楚如何做到这一点。

7 个答案:

答案 0 :(得分:49)

我明白了。它很尴尬,但很简单...... 临时解决方案可能是这样的:

 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()));
    }
  }
}

如果您知道如何改进此代码 - 请随时写一下。

答案 1 :(得分:32)

可能的解决方案是使用JsonElement作为Callback类型(Callback<JsonElement>)。在您的原始示例中:

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

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

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

答案 2 :(得分:30)

  

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);
}

答案 3 :(得分:21)

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

在声明中使用Response如下:

... Callback<Response> callback);

在处理回复时:

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

答案 4 :(得分:4)

当@lordmegamax回答完全有效时,有更好的解决方案来自

  

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);
  }
}

答案 5 :(得分:-1)

这是我在调试器中调试之后所做的。注意:这是为了实际将其置于错误回调中,而不是成功回调。

您可以通过调用retrofitError.getSuccessType()并返回Type类型的对象来查看成功类型

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

以下是单行答案:

retrofitError.getBodyAs(retrofitError.getSuccessType())

现在,我注意到我不必对成功回调做任何类似的事情,因为它已经神奇地工作了。

答案 6 :(得分:-1)

获取Call JSONObject或JSONArray

您可以创建自定义工厂或从此处复制它:https://github.com/marcinOz/Retrofit2JSONConverterFactory