You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Retrofit+Data Binding下RecyclerView无API数据及JSON解析异常排查

Fixing "Expected BEGIN_ARRAY but was BEGIN_OBJECT" JSON Parsing Error with Retrofit

Hey there, let's break down and fix this issue you're facing. That error message tells us exactly what's going wrong: your app is expecting a JSON array (BEGIN_ARRAY) but the API is returning a JSON object (BEGIN_OBJECT) instead. Let's walk through the fixes step by step.

Why This Happens

Looking at your Weather entity class, it's structured to represent a single weather entry (with fields like coord, name, main). But in your Repository, you're using Call<List<Weather>> to handle the response—this tells Retrofit/Gson to parse the incoming JSON as an array of Weather objects, which doesn't match what the API is sending back (a single object).

On top of that, you weren't updating the MutableLiveData with the downloaded data, which is why your RecyclerView stayed empty even when the API call succeeded.

Step 1: Fix the Retrofit Call Type

First, update your API interface and Repository to expect a single Weather object instead of a list:

Update your ApiInterface

Change the return type from Call<List<Weather>> to Call<Weather>:

public interface ApiInterface {
    // Adjust the @GET path if you're actually using an hourly forecast endpoint (e.g., "forecast/hourly")
    @GET("weather")
    Call<Weather> getHourlyForecast(
        @Query("q") String q,
        @Query("appid") String appid,
        @Query("units") String units
    );
}

Update your Repository Code

Modify the callback to handle a single Weather object, then wrap it in a list to match your MutableLiveData<List<Weather>> type, and don't forget to update the LiveData:

public MutableLiveData<List<Weather>> getHourlyForecast(String q, String appid, String units) {
    final MutableLiveData<List<Weather>> data = new MutableLiveData<>();
    apiInterface.getHourlyForecast(q, appid, units).enqueue(new Callback<Weather>() {
        @Override
        public void onResponse(Call<Weather> call, Response<Weather> response) {
            if (response.isSuccessful() && response.body() != null) {
                // Wrap the single Weather object in a list to match LiveData's type
                List<Weather> weathers = Collections.singletonList(response.body());
                data.postValue(weathers); // This tells the RecyclerView new data is available
                Log.e("HFR onResponse ", "Data loaded successfully: " + response.body().getName());
            } else {
                Log.e("HFR onResponse ", "Failed to load data. Response code: " + response.code());
            }
        }

        @Override
        public void onFailure(Call<Weather> call, Throwable t) {
            Log.e("HFR onFailure ", t.getMessage());
            data.postValue(null); // Update LiveData on failure to avoid hanging states
        }
    });
    return data;
}

Wait—Are You Actually Fetching Hourly Forecasts?

If your goal is to get hourly forecast data (not just current weather), the API likely returns a parent object containing an array of hourly entries. For example, many weather APIs return a JSON object with a list field that holds the hourly data array. In that case, you'll need a wrapper entity class:

Create a Wrapper Class for Hourly Forecasts

public class HourlyForecastResponse {
    @SerializedName("list")
    @Expose
    private List<Weather> hourlyForecasts;

    // Getter and setter
    public List<Weather> getHourlyForecasts() {
        return hourlyForecasts;
    }

    public void setHourlyForecasts(List<Weather> hourlyForecasts) {
        this.hourlyForecasts = hourlyForecasts;
    }
}

Update API Interface and Repository for Hourly Forecasts

// ApiInterface update
@GET("forecast/hourly")
Call<HourlyForecastResponse> getHourlyForecast(
    @Query("q") String q,
    @Query("appid") String appid,
    @Query("units") String units
);

// Repository update
public MutableLiveData<List<Weather>> getHourlyForecast(String q, String appid, String units) {
    final MutableLiveData<List<Weather>> data = new MutableLiveData<>();
    apiInterface.getHourlyForecast(q, appid, units).enqueue(new Callback<HourlyForecastResponse>() {
        @Override
        public void onResponse(Call<HourlyForecastResponse> call, Response<HourlyForecastResponse> response) {
            if (response.isSuccessful() && response.body() != null) {
                data.postValue(response.body().getHourlyForecasts());
                Log.e("HFR onResponse ", "Loaded " + response.body().getHourlyForecasts().size() + " hourly forecast items");
            }
        }

        @Override
        public void onFailure(Call<HourlyForecastResponse> call, Throwable t) {
            Log.e("HFR onFailure ", t.getMessage());
            data.postValue(null);
        }
    });
    return data;
}

Final Checks

  • Double-check your API endpoint to confirm the JSON structure (use tools like Postman or curl to see the raw response).
  • Ensure your Weather entity class matches the fields in the API's JSON response (especially if using hourly forecasts—some fields might differ from current weather).
  • Verify that your RecyclerView's adapter is observing the MutableLiveData correctly and updating when the data changes.

内容的提问来源于stack exchange,提问作者user9897182

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:48:57