英文:
Parse JSON string to a list of objects in Kotlin Android (MOSHI?)
问题
在我的应用中,我正在使用 Retrofit 从 API 获取数据(包含航班数据)。我想从 JSON 中获取 List<Itinerary>,但问题是它的格式很糟糕,我得到的行程安排是分开的。我听说可以使用 Moshi 库来解决这个问题,但我不知道怎么做。
data class ItineraryData(
    val itinerary_0: Itinerary0,
    val itinerary_1: Itinerary0,
    val itinerary_2: Itinerary0,
    val itinerary_3: Itinerary0,
    val itinerary_4: Itinerary0,
    val itinerary_5: Itinerary0,
    val itinerary_6: Itinerary0,
    val itinerary_7: Itinerary0,
    val itinerary_8: Itinerary0,
    val itinerary_9: Itinerary0,
)
我想要的是:
data class ItineraryData(
    val itineraries: List<Itinerary0>
)
JSON 片段:
"itinerary_data": {
      "itinerary_0": {...},
      "itinerary_1": {...},
      "itinerary_2": {...},
      "itinerary_3": {...},
      "itinerary_4": {...},
      "itinerary_5": {...},
      "itinerary_6": {...},
      "itinerary_7": {...},
      "itinerary_8": {...},
      "itinerary_9": {...},
}
Retrofit 应用 Api:
@Provides
    @Singleton
    fun provideFlightApi(): FlightApi {
        val loggingInterceptor = HttpLoggingInterceptor().apply {
            level = HttpLoggingInterceptor.Level.BODY
        }
        val client = OkHttpClient.Builder()
            .addInterceptor(loggingInterceptor)
            .build()
        return Retrofit.Builder()
            .baseUrl(Constants.BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .client(client)
            .build()
            .create(FlightApi::class.java)
    }
getFlights 函数:
@GET(value = "v2/flight/departures")
    suspend fun getFlights(
        @Query("rapidapi-key") apiKey: String = BuildConfig.API_KEY,
        @Query("departure_date") date: String,
        @Query("adults") passengers: Int,
        @Query("sid") sid: String = "SIFjfID63",
        @Query("origin_city_id") cityDep: String,
        @Query("destination_city_id") cityArr: String,
        @Query("number_of_itineraries") itinerariesCount: Int = 1
    ) : ApiResponse2
英文:
In my app, I'm using Retrofit to get the data from API (with flights data). I want to obatin List< Itinerary > from JSON, but the problem is that it is badly formatted, and I'm getting the itineraries seperately. I heard that it's possible to do that with a Moshi library, but I don't know how to do that.
data class ItineraryData(
    val itinerary_0: Itinerary0,
    val itinerary_1: Itinerary0,
    val itinerary_2: Itinerary0,
    val itinerary_3: Itinerary0,
    val itinerary_4: Itinerary0,
    val itinerary_5: Itinerary0,
    val itinerary_6: Itinerary0,
    val itinerary_7: Itinerary0,
    val itinerary_8: Itinerary0,
    val itinerary_9: Itinerary0,
)
WHAT I WANT:
data class ItineraryData(
    val itineraries: List<Itinerary0>
)
JSON FRAGMENT
"itinerary_data" : {
      "itinerary_0": {...},
      "itinerary_1": {...},
      "itinerary_2": {...},
      "itinerary_3": {...},
      "itinerary_4": {...},
      "itinerary_5": {...},
      "itinerary_6": {...},
      "itinerary_7": {...},
      "itinerary_8": {...},
      "itinerary_9": {...},
}"
Retrofit App Api:
@Provides
    @Singleton
    fun provideFlightApi(): FlightApi {
        val loggingInterceptor = HttpLoggingInterceptor().apply {
            level = HttpLoggingInterceptor.Level.BODY
        }
        val client = OkHttpClient.Builder()
            .addInterceptor(loggingInterceptor)
            .build()
        return Retrofit.Builder()
            .baseUrl(Constants.BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .client(client)
            .build()
            .create(FlightApi::class.java)
    }
getFlights function:
@GET(value = "v2/flight/departures")
    suspend fun getFlights(
        @Query("rapidapi-key") apiKey: String = BuildConfig.API_KEY,
        @Query("departure_date") date: String,
        @Query("adults") passengers: Int,
        @Query("sid") sid: String = "SIFjfID63",
        @Query("origin_city_id") cityDep: String,
        @Query("destination_city_id") cityArr: String,
        @Query("number_of_itineraries") itinerariesCount: Int = 1
    ) : ApiResponse2
答案1
得分: 2
以下是翻译好的部分:
"It sounds like the number of itinerary objects you receive from the server is variable (not necessarily always 10). While it's unfortunate, then, that the server returns these in a JSON object instead of a variable-length array, a custom Moshi JsonAdapter can read and fix this."
这句话的意思是:看起来你从服务器接收到的行程对象的数量是可变的(不一定总是10个)。尽管这很不幸,服务器将它们以JSON对象的方式返回,而不是以可变长度的数组返回,但自定义的Moshi JsonAdapter可以读取和修复这个问题。
英文:
It sounds like the number of itinerary objects you receive from the server is variable (not necessarily always 10). While it's unfortunate, then, that the server returns these in a JSON object instead of a variable-length array, a custom Moshi JsonAdapter can read and fix this.
val json = """
  {
    "itinerary_data" : {
      "itinerary_0": {},
      "itinerary_1": {},
      "itinerary_2": {},
      "itinerary_3": {},
      "itinerary_4": {},
      "itinerary_5": {},
      "itinerary_6": {},
      "itinerary_7": {},
      "itinerary_8": {},
      "itinerary_9": {},
      "itinerary_10": {},
      "itinerary_11": {}
    }
  }
""".trimIndent()
fun main() {
  // This is the Moshi object to give to your Retrofit converter.
  val moshi = Moshi.Builder()
    .add(ItineraryData.Adapter)
    .build()
  val itineraryDataAdapter = moshi.adapter(ItineraryData::class.java)
  val itineraryData = itineraryDataAdapter.fromJson(json)
}
@JsonClass(generateAdapter = true)
data class ItineraryData(
  val itineraries: List<Itinerary>
) {
  object Adapter {
    @FromJson
    fun fromJson(
      reader: JsonReader,
      itineraryAdapter: JsonAdapter<Itinerary>
    ): ItineraryData? {
      if (reader.peek() == JsonReader.Token.NULL) {
        return reader.nextNull<ItineraryData?>()
      }
      reader.beginObject()
      var itineraries: MutableList<Itinerary>? = null
      while (reader.hasNext()) {
        when (reader.selectName(options)) {
          0 -> {
            // Found the itinerary_data field.
            reader.beginObject()
            itineraries = mutableListOf()
            while (reader.hasNext()) {
              val name = reader.nextName()
              if (name.startsWith("itinerary_")) {
                itineraries += itineraryAdapter.fromJson(reader)!!
              } else {
                // Throw away this non-itinerary field we are not using.
                reader.skipValue()
              }
            }
            reader.endObject()
          }
          -1 -> {
            // Throw away this field we are not using.
            reader.skipName()
            reader.skipValue()
          }
          else -> {
            throw AssertionError()
          }
        }
      }
      reader.endObject()
      if (itineraries == null) {
        throw JsonDataException("Missing itinerary_data field.")
      }
      return ItineraryData(unmodifiableList(itineraries))
    }
    @ToJson
    fun toJson(
      writer: JsonWriter,
      value: ItineraryData?,
      itineraryAdapter: JsonAdapter<Itinerary>
    ) {
      if (value == null) {
        writer.nullValue()
        return
      }
      writer.beginObject()
      writer.name("itinerary_data")
      writer.beginObject()
      for (i in value.itineraries.indices) {
        val itinerary = value.itineraries[i]
        writer.name("itinerary_$i")
        itineraryAdapter.toJson(writer, itinerary)
      }
      writer.endObject()
      writer.endObject()
    }
    private val options = JsonReader.Options.of(
      "itinerary_data"
    )
  }
}
@JsonClass(generateAdapter = true)
class Itinerary {
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论