Retrofit对象返回为空

3

我已经建立了一个RetroFitService来返回一些对象。在MainActivity中,我通过简单的按钮点击调用服务。我似乎得到了某种对象,但我感觉它实际上并没有从我指定的REST API返回。它出现在调试器中,但其属性为null:

bFetch.setOnClickListener(v -> {
 v.startAnimation(AnimationUtils.loadAnimation(this, R.anim.image_click));
 RetrofitService service = ServiceFactory.createRetrofitService(RetrofitService.class, RetrofitService.SERVICE_ENDPOINT);
 service.getPosts()
 .subscribeOn(Schedulers.newThread())
 .observeOn(AndroidSchedulers.mainThread())
 .subscribe(new Subscriber < Post > () {
  @Override
  public final void onCompleted() {
   Log.e("RetrofitService", "Retrofit Request Completed!");
  }

  @Override
  public final void onError(Throwable e) {
   Log.e("RetrofitService", e.getMessage());
  }

  @Override
  public final void onNext(Post post) {
   if (post != null) {
    // TODO: Some object is returned but its properties are null
    Log.e("RetrofitService", "Returned objects: " + post);
    Log.e("RetrofitService", "Object Id: " + post.getObjectId());
    mCardAdapter.addData(post);
   } else {
    Log.e("RetrofitService", "Object returned is null.");
   }

  }

 });

});

}

enter image description here

Service:

public interface RetrofitService {

 String SERVICE_ENDPOINT = "https://parseapi.back4app.com/";

 @Headers({
  "X-Parse-Application-Id: asdf",
  "X-Parse-REST-API-Key: asdf"
 })
 @GET("/classes/Post")
 Observable < Post > getPosts();

 /*curl -X GET \
         -H "X-Parse-Application-Id: asdf" \
         -H "X-Parse-REST-API-Key: asdf" \
 https://parseapi.back4app.com/classes/Post*/

}

curl命令很好用,我没有遇到任何错误。问题可能出在哪里?我的@GET方法有问题吗?

为了完整起见,这是ServiceFactory类:

public class ServiceFactory {

    /**
     * Creates a retrofit service from an arbitrary class (clazz)
     * @param clazz Java interface of the retrofit service
     * @param endPoint REST endpoint url
     * @return retrofit service with defined endpoint
     */
    public static <T> T createRetrofitService(final Class<T> clazz, final String endPoint) {
        final RestAdapter restAdapter = new RestAdapter.Builder()
                .setEndpoint(endPoint)
                .build();
        T service = restAdapter.create(clazz);

        return service;
    }
}

我的build.gradle文件,因为我知道不同版本的Retrofit存在不一致性:

dependencies {

    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.3.1'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    testCompile 'junit:junit:4.12'

    /* ReactiveX */
    compile 'io.reactivex:rxjava:1.0.17'
    compile 'io.reactivex:rxandroid:0.23.0'

    /* Retrofit */
    compile 'com.squareup.retrofit:retrofit:1.9.0'

    /* OkHttp3 */
    compile 'com.squareup.okhttp3:okhttp:3.8.1'

    /* RecylerView */
    compile 'com.android.support:recyclerview-v7:25.3.1'

    /* CardView */
    compile 'com.android.support:cardview-v7:25.3.1'

    /* Parse */
    compile 'com.parse:parse-android:1.13.0'

}

文章类:

public class Post implements Serializable {

    private static final String CLASS_NAME = "Post";

    private String objectId;
    private String text;

    public Post(String objectId) {
        this.setObjectId(objectId);
    }

    public static String getClassName() {
        return CLASS_NAME;
    }

    public String getObjectId() {
        return objectId;
    }

    private void setObjectId(String objectId) {
        this.objectId = objectId;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

响应结果:

>     https://parseapi.back4app.com/classes/Post/
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   259  100   259    0     0    360      0 --:--:-- --:--:-- --:--:--   395{"results":[{"objectId":"ktEfgr1pFt","text":"Hello World.","createdAt":"2017-08-14T14:07:52.826Z","updatedAt":"2017-08-14T14:07:52.826Z"},{"objectId":"Mmh8l9gjCk","text":"Hello?","createdAt":"2017-08-14T15:19:01.515Z","updatedAt":"2017-08-14T15:19:03.743Z"}]}

最终更新:我已经修改了RetrofitServiceonNext()方法,使其能够传递给CardAdapter,尽管这里没有显示它,并超出了问题的范围。

@Override
public final void onNext(PostResponse postResponse) {
 if (postResponse != null) {
  // TODO: Some object is returned but its properties are null
  Log.e("RetrofitService", "Objects successfully added to RecyclerView Adapter.");
  Log.e("RetrofitService", "Returned objects: " + postResponse.getResults());
  Log.e("RetrofitService", "Text " + postResponse.getResults().get(0).getText());

  mCardAdapter.addData(postResponse);
  //
 } else {
  Log.e("RetrofitService", "Object returned is null.");
 }

}

看起来你正在尝试获取由Parse SDK创建的对象。请确保你的帖子类(Post class)的数据结构与端点返回的内容匹配(例如使用String)。另外,我建议您在问题中掩盖URL和凭据。 - Torsten Ojaperv
1
我用帖子对象更新了我的问题。我真的没有看出有什么问题... - Martin Erlic
你可以提供从以下curl命令获取的json响应吗? curl -X GET -H "X-Parse-Application-Id: asdf" -H "X-Parse-REST-API-Key: asdf" https://parseapi.back4app.com/classes/Post - Torsten Ojaperv
再次更新,附带JSON响应。 - Martin Erlic
1
我更新了我的注释。错误现在很明显:你应该创建一个包含Post对象列表的包装器类,用于包装包装器对象。 - Клаус Шварц
2个回答

3
请在RetrofitService中尝试使用以下类。
@GET("/classes/Post")
Observable <PostResponse> getPosts();

PostResponse封装类
public class PostResponse {
    private List<Post> results;

    public List<Post> getResults() {
        return results;
    }

    public void setResults(List<Post> results) {
        this.results = results;
    }
}

你们让我很难奖励这个问题。两个答案都有帮助。是的,我确实需要在你提供的类中包装我返回的列表。 - Martin Erlic
我认为Torsten比我更值得获得奖励。他回答得更快 :) - Клаус Шварц
1
很酷,但我认为答案还应该包括关于尾部斜杠的部分,这样其他人也能理解。谢谢大家。 - Martin Erlic

1
根据您问题的更新,您收到的不是单个对象,而是一个包含多个Post对象的集合。

因此,您需要再添加一个类:

public class Results {
   List<Post> results = ArrayList<>()
} 

然后更新您的 API 接口方法,使其返回 Observable<Results>

Observable <Results> getPosts();

在订阅内,您最终可以通过results字段访问Result对象,其中包含Post对象的集合。


一个小错误是你的端点基础URL末尾有斜杠:
String SERVICE_ENDPOINT = "https://parseapi.back4app.com/";

同时,您的API方法在路径开头有斜杠:

 @GET("/classes/Post")
 Observable < Post > getPosts();

据我所记,使用retrofit时,端点地址应该在末尾加斜杠或API方法前加斜杠,否则可能无法正常工作。
这个应该是正确的:
String SERVICE_ENDPOINT = "https://parseapi.back4app.com"; // No slash here
@GET("/classes/Post") // Keep slash here, or vice verse
Observable<Results> getPosts();

关于您的依赖项部分,以下是一些评论:

您使用的 io.reactivex:rxandroid 版本为 0.23.0,而最新稳定版本为 1.2.1,是否有特殊原因?

由于 retrofit 1.9.0 已经依赖于 rxjava 1.0.0,因此您不需要 io.reactivex:rxjava:1.0.17 依赖项。


谢谢。你说的尾随斜杠是对的。我整理了我的依赖关系。我只使用0.23.0,因为在以前的项目中(稍微不同的实现)中它可以工作。看起来Retrofit最近经历了很多变化。不幸的是,我仍然得到空对象属性。也许是因为我返回了一个列表,而没有像Torsten所指示的那样进行处理。 - Martin Erlic
只是一个提示,根据我的监视器显示,“RetrofitService.getPosts: URL路径“classes/Post”必须以'/'开头。” - Martin Erlic
@santafebound,您发布了JSON响应正文,我已更新我的答案。您将Post对象的集合序列化为另一个对象中的result字段,这是将其转换为单个Post对象的真正错误,而不是尾随斜杠。 - Клаус Шварц

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接