Retrofit 2:response.body()为空,但状态码为200。

3

我不确定为什么会出现这种情况,我正在尝试从以下获取数据:

https://api.stackexchange.com/2.2/questions?order=desc&sort=creation&site=stackoverflow&tagged=android

使用以下代码:
public interface StackOverflowAPI {

    @GET("/2.2/questions?order=desc&sort=creation&site=stackoverflow")
    Call<StackQuestions> loadQuestions(@Query("tagged") String tags);

}

和类:

public class StackQuestion {

    @SerializedName("title")
    private String title;

    @SerializedName("view_count")
    private int numViews;

    @SerializedName("is_answered")
    private boolean solved;


    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public int getNumViews() {
        return numViews;
    }

    public void setNumViews(int numViews) {
        this.numViews = numViews;
    }

    public boolean isSolved() {
        return solved;
    }

    public void setSolved(boolean solved) {
        this.solved = solved;
    }

}


public class StackQuestions {
    List<StackQuestion> questions;
}

片段:

public class AlphaFragment extends Fragment implements Callback<StackQuestions> {

    private StackOverflowAPI stackOverflowAPI;

    private EditText    etxtQuestionToSearch;
    private Button      btnSearchQuestion;

    private RecyclerView                mRecyclerView;
    private RecyclerView.Adapter        mStackQuestionsAdapter;
    private RecyclerView.LayoutManager  mLayoutManager;

    private ArrayList<StackQuestion> mStackQuestionsList;

    private final static String TAG = "AlphaFragment";

    public AlphaFragment() {}


    // ------------------------ FRAGMENT LIFECYCLE ------------------------ //

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        init();
    } // end : onCreate Method

    @Override
    public View onCreateView(LayoutInflater inflater,
                             ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_alpha, container, false);

        etxtQuestionToSearch = (EditText) view.findViewById(R.id.etxt_stack_topic);
        btnSearchQuestion = (Button) view.findViewById(R.id.button_stack_search);
        mRecyclerView = (RecyclerView) view.findViewById(R.id.rcvw_stackquestions_list);

        // Recycler View SETUP
        mLayoutManager = new LinearLayoutManager(getActivity());
        mRecyclerView.setLayoutManager(mLayoutManager);
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());

        btnSearchQuestion.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                searchStackQuestion();
            } // end : onClick Method
        });

        return view;
    } // end : onCreateView Method


    // ------------------------- CALLBACK METHODS ------------------------- //

    @Override
    public void onResponse(Call<StackQuestions> call,
                           Response<StackQuestions> response) {
        if (response == null) {
            return;
        }

        List<StackQuestion> resultList = response.body().questions;

        // X:ERROR: .questions == NULL, so Toast appears
        if (resultList == null || resultList.isEmpty()) {
            Toast.makeText(getActivity(), "Results for the query are null/empty", Toast.LENGTH_LONG).show();
            return;
        }

        // Add resulting data from web service
        for (StackQuestion question : resultList) {
            mStackQuestionsList.add(question);
        }

        // Assign data to recycler view
        mStackQuestionsAdapter = new StackQuestionsAdapter(
            mStackQuestionsList,
            getActivity(),
            new RVTapListener() {
                @Override
                public void onItemClick(View v, int position) {
                    Toast.makeText(getActivity(), "Now clicked, but not implemented " + new Date(), Toast.LENGTH_SHORT).show();
                } // end : onItemClick Method
            }
        );
        mRecyclerView.setAdapter(mStackQuestionsAdapter);
    } // end : onResponse Method

    @Override
    public void onFailure(Call<StackQuestions> call,
                          Throwable t) {
        Toast.makeText(getActivity(), t.getLocalizedMessage(), Toast.LENGTH_LONG).show();
    } // end : onFailure Method


    // ------------------------- FRAGMENT METHODS ------------------------- //

    private void init() {
        Gson gson = new GsonBuilder()
            .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
            .create();

        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();

        Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("https://api.stackexchange.com")
            .addConverterFactory(GsonConverterFactory.create(gson))
            .client(client)
            .build();

        stackOverflowAPI = retrofit.create(StackOverflowAPI.class);
    } // end : init Method

    private void searchStackQuestion() {
        mStackQuestionsList = new ArrayList<>();

        Call<StackQuestions> call = stackOverflowAPI.loadQuestions("android");
        call.enqueue(this);
    } // end : searchStackQuestion Method

}    

Gradle依赖项:

compile 'com.google.code.gson:gson:2.7'
compile 'com.android.support:design:24.0.0'
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.android.support:cardview-v7:24.0.0'
compile 'com.android.support:appcompat-v7:24.0.0'
compile 'com.android.support:recyclerview-v7:24.0.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.3.1'

执行代码后,我在logcat中得到了以下内容:
D/OkHttp: <-- 200 OK https://api.stackexchange.com/2.2/questions?order=desc&sort=creation&site=stackoverflow&tagged=android (1136ms)
D/OkHttp: Cache-Control: private
D/OkHttp: Content-Type: application/json; charset=utf-8
D/OkHttp: Access-Control-Allow-Origin: *
D/OkHttp: Access-Control-Allow-Methods: GET, POST
D/OkHttp: Access-Control-Allow-Credentials: false
D/OkHttp: X-Content-Type-Options: nosniff
D/OkHttp: Date: Tue, 28 Jun 2016 22:56:26 GMT
D/OkHttp: {"items":[{"tags":["android"],"owner":{"reputation":1,"user_id"  ...
D/OkHttp: er_id":1163471,"user_type":"registered","accept_rate":93,"profil ...
D/OkHttp: e?type=large","display_name":"Vahid Zadeh","link":"http://stacko ...
D/OkHttp: n_id":38086882,"link":"stackoverflow.com/questions/380868 ...
D/OkHttp: ","animation-listener"],"owner":{"reputation":23,"user_id":43462 ...
D/OkHttp: <-- END HTTP (19295-byte body)

一切都很顺利,但我在AlphaFragment的这个部分遇到了NullPointerException异常:
List<StackQuestion> resultList = response.body().questions;

// X:ERROR: .questions == NULL, so Toast appears
if (resultList == null || resultList.isEmpty()) {
    Toast.makeText(getActivity(), "Results for the query are null/empty", Toast.LENGTH_LONG).show();
    return;
}

有什么问题吗?我得到了状态码为 200 ,而且JSON信息就在那里;但是,在尝试检索它之后,出现了NPE

提前致谢...


在 Postman 或 Hurlit 中的响应是什么,@Nelson? - Lips_coder
2个回答

3
我认为您的模型存在错误。
尝试以下操作:
public class StackQuestions {
    @SerializedName("items")
    List<StackQuestion> questions;
}

0

Retrofit 默认使用 Gson 解析 JSON,所以要成功解析 JSON,你需要在相同的层次结构中定义模型。

我想像这样做应该可以:

public class StackQuestions {
    private List<StackQuestion> items;

    public void setItems(List<StackQuestion> items){
        this.items = items;
    }
    public List<StackQuestions> getItems(){
        return this.items;
    }
}


public class StackQuestion{
    private Owner owner;

    public void setOwner(Owner owner){
        this.owner = owner;
    }
    public Owner getOwner(){
        return this.owner;
    }
}


public class Owner {

    @SerializedName("title")
    private String title;

    @SerializedName("view_count")
    private int numViews;

    @SerializedName("is_answered")
    private boolean solved;


    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public int getNumViews() {
        return numViews;
    }

    public void setNumViews(int numViews) {
        this.numViews = numViews;
    }

    public boolean isSolved() {
        return solved;
    }

    public void setSolved(boolean solved) {
        this.solved = solved;
    }

}

我遇到了同样的问题(响应代码为空,但我可以在我的okHTTP logcat中看到JSON记录)。我正在访问的REST API(googlemaps timezone)只有一个项目需要加载到对象中,而该项目只有5个字段。换句话说,不需要数组或列表。我无法弄清楚如何对对象进行建模,以便从API获取数据。我已经为使用LIST的另一个API解决了所有这些问题。但是对于这个谷歌(非LIST)API,我无法找到正确的模型。请帮忙! - Frank Zappa
我遇到了同样的问题,从一个电台的 json api 中检索一些“正在播放”的信息。虽然从 okhttp3 中得到 200 并正确返回 json,但 retrofit (2.4.0) 总是将其转换为空响应。希望有人有一种通用的方法来解决这个问题。在 retrofit 的文档中找不到任何信息。 - carl

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