android - 将 JSON 字符串解析为 Kotlin Android 中的对象列表(MOSHI?)

标签 android json kotlin retrofit 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 fragment

"itinerary_data" : {
      "itinerary_0": {...},
      "itinerary_1": {...},
      "itinerary_2": {...},
      "itinerary_3": {...},
      "itinerary_4": {...},
      "itinerary_5": {...},
      "itinerary_6": {...},
      "itinerary_7": {...},
      "itinerary_8": {...},
      "itinerary_9": {...},
}"

改造应用程序 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

最佳答案

听起来您从服务器收到的行程对象的数量是可变的(不一定总是 10)。虽然不幸的是,服务器以 JSON 对象而不是可变长度数组返回这些内容,但自定义 Moshi JsonAdapter 可以读取并修复此问题。

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 {
}

关于android - 将 JSON 字符串解析为 Kotlin Android 中的对象列表(MOSHI?),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75500629/

相关文章:

android - 在我触摸之前,android 中的 ListView 不显示项目

javascript - 将 JSON 插入 HTML 数据属性

kotlin - Kotlin channel 是否用于协程线程安全/同步/保持发生在关系之前?

reflection - 在 Kotlin 中按名称动态获取函数

java - 如何将 gif 转换为动态壁纸以在 Android 应用程序中使用

android - OverridePendingTransition,当前 Activity 位于顶部

java.lang.IllegalArgumentException : Cannot determine the graph element type because the document class is null.

json - 在 Wonderware 的 ArchestrA/Quickscript.NET 中反序列化 JSON

json - 在 Swift 中解析 JSON 数据(来自 URL)

kotlin - 问:检查Kotlin中变量是否为null的惯用方式