androidretrofit2android-roomdagger-hiltrx-java3

com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 148 path $.main


i have problem and really don't know how to fix this. I try to find similar posts several days, but didn't find.

I use retrofit for parsing api and put it in room database and use rxjava3 because it will be asynchronously

That my JSON

{
  "coord": {
    "lon": -0.1257,
    "lat": 51.5085
  },
  "weather": [
    {
      "id": 803,
      "main": "Clouds",
      "description": "broken clouds",
      "icon": "04d"
    }
  ],
  "base": "stations",
  "main": {
    "temp": 22.78,
    "feels_like": 22.81,
    "temp_min": 21.23,
    "temp_max": 23.92,
    "pressure": 1020,
    "humidity": 65
  },
  "visibility": 10000,
  "wind": {
    "speed": 0.45,
    "deg": 264,
    "gust": 2.68
  },
  "clouds": {
    "all": 75
  },
  "dt": 1623415339,
  "sys": {
    "type": 2,
    "id": 2019646,
    "country": "GB",
    "sunrise": 1623383015,
    "sunset": 1623442617
  },
  "timezone": 3600,
  "id": 2643743,
  "name": "London",
  "cod": 200
}

Api service

interface OpenWeatherApiService {

@GET("weather")
fun getCurrentWeather(
    @Query("q") location:String,
    @Query("appid") key:String,
    @Query("units") units:String,
    @Query("lang") language:String = "en"
):Observable<CurrentWeatherResponse>}

That my app module

@Module
@InstallIn(ActivityComponent::class)
object AppModule {
@Provides
fun provideOkHttpClient() =if(BuildConfig.DEBUG) {
    val interceptor = HttpLoggingInterceptor()
    interceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
    OkHttpClient
        .Builder()
        .addInterceptor(interceptor)
        .build()
}else{
    OkHttpClient
        .Builder()
        .build()
}
@Provides
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit = Retrofit.Builder()
    .baseUrl("https:/api.openweathermap.org/data/2.5/")
    .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
    .addConverterFactory(GsonConverterFactory.create())
    .client(okHttpClient)
    .build()

@Provides
fun provideGson(): Gson = GsonBuilder().create()

@Provides
fun provideOpenWeatherApiService(retrofit: Retrofit):OpenWeatherApiService = retrofit.create(OpenWeatherApiService::class.java)

@Provides
fun provideOpenWeatherApiHelper(openWeatherApiHelper: OpenWeatherApiHelperImpl):OpenWeatherApiHelper = openWeatherApiHelper

@Provides
fun provideForecastDatabase(@ApplicationContext appContext: Context) = ForecastDatabase.getDatabase(appContext)

@Provides
fun provideCurrentWeatherDao(db: ForecastDatabase) = db.currentWeatherDao()


}

My response

const val CURRENT_WEATHER_ID = 0

@Entity(tableName = "current_weather")
data class CurrentWeatherResponse(
        val main: List<Main> ,
        val name: String? = "",
        val visibility: Int? = 0,
        val weather: List<Weather> ,
        val wind:List<Wind>
){
    @PrimaryKey(autoGenerate = false)
    var id_current_weather:Int = CURRENT_WEATHER_ID
}

My type converter for Main, i put it in on a database

class MainConverter {
    val gson = Gson()

    @TypeConverter
    fun listMainToString(mainList: List<Main?>?):String?{
        return gson.toJson(mainList)
    }
    @TypeConverter
    fun stringToListMain(dataMain:String?):List<Main?>?{
        if (dataMain ==  null){
            return Collections.emptyList()
        }
        val listType: Type = object :
                TypeToken<List<Main>?>() {}.type
        return gson.fromJson<List<Main?>?>(dataMain,listType)
    }
}

Solution

  • The data class you are generating for your JSON response is not correct. Many of the things are objects, but you have assigned it as a List item. Here is the correct data class response based on your JSON. So the JSON response is not being parsed properly.

    data class CurrentWeatherResponse(
        val base: String,
        val clouds: Clouds,
        val cod: Int,
        val coord: Coord,
        val dt: Int,
        val id: Int,
        val main: Main,
        val name: String,
        val sys: Sys,
        val timezone: Int,
        val visibility: Int,
        val weather: List<Weather>,
        val wind: Wind
    )
    
    data class Clouds(
        val all: Int
    )
    
    data class Coord(
        val lat: Double,
        val lon: Double
    )
    
    data class Main(
        val feels_like: Double,
        val humidity: Int,
        val pressure: Int,
        val temp: Double,
        val temp_max: Double,
        val temp_min: Double
    )
    
    data class Sys(
        val country: String,
        val id: Int,
        val sunrise: Int,
        val sunset: Int,
        val type: Int
    )
    
    data class Weather(
        val description: String,
        val icon: String,
        val id: Int,
        val main: String
    )
    
    data class Wind(
        val deg: Int,
        val gust: Double,
        val speed: Double
    )
    

    I suggest you try to use this and try again. Here is also a plugin that automatically generates data classes based of JSON. It might help you more.

    https://plugins.jetbrains.com/plugin/10054-generate-kotlin-data-classes-from-json