在Kotlin Android中将JSON字符串解析为对象列表(使用MOSHI?)

huangapple go评论54阅读模式
英文:

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&lt;Itinerary0&gt;
)

JSON FRAGMENT

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

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 = &quot;v2/flight/departures&quot;)
    suspend fun getFlights(
        @Query(&quot;rapidapi-key&quot;) apiKey: String = BuildConfig.API_KEY,
        @Query(&quot;departure_date&quot;) date: String,
        @Query(&quot;adults&quot;) passengers: Int,
        @Query(&quot;sid&quot;) sid: String = &quot;SIFjfID63&quot;,
        @Query(&quot;origin_city_id&quot;) cityDep: String,
        @Query(&quot;destination_city_id&quot;) cityArr: String,
        @Query(&quot;number_of_itineraries&quot;) 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 = &quot;&quot;&quot;
  {
    &quot;itinerary_data&quot; : {
      &quot;itinerary_0&quot;: {},
      &quot;itinerary_1&quot;: {},
      &quot;itinerary_2&quot;: {},
      &quot;itinerary_3&quot;: {},
      &quot;itinerary_4&quot;: {},
      &quot;itinerary_5&quot;: {},
      &quot;itinerary_6&quot;: {},
      &quot;itinerary_7&quot;: {},
      &quot;itinerary_8&quot;: {},
      &quot;itinerary_9&quot;: {},
      &quot;itinerary_10&quot;: {},
      &quot;itinerary_11&quot;: {}
    }
  }
&quot;&quot;&quot;.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&lt;Itinerary&gt;
) {
  object Adapter {
    @FromJson
    fun fromJson(
      reader: JsonReader,
      itineraryAdapter: JsonAdapter&lt;Itinerary&gt;
    ): ItineraryData? {
      if (reader.peek() == JsonReader.Token.NULL) {
        return reader.nextNull&lt;ItineraryData?&gt;()
      }
      reader.beginObject()
      var itineraries: MutableList&lt;Itinerary&gt;? = null
      while (reader.hasNext()) {
        when (reader.selectName(options)) {
          0 -&gt; {
            // Found the itinerary_data field.
            reader.beginObject()
            itineraries = mutableListOf()
            while (reader.hasNext()) {
              val name = reader.nextName()
              if (name.startsWith(&quot;itinerary_&quot;)) {
                itineraries += itineraryAdapter.fromJson(reader)!!
              } else {
                // Throw away this non-itinerary field we are not using.
                reader.skipValue()
              }
            }
            reader.endObject()
          }
          -1 -&gt; {
            // Throw away this field we are not using.
            reader.skipName()
            reader.skipValue()
          }
          else -&gt; {
            throw AssertionError()
          }
        }
      }
      reader.endObject()
      if (itineraries == null) {
        throw JsonDataException(&quot;Missing itinerary_data field.&quot;)
      }
      return ItineraryData(unmodifiableList(itineraries))
    }

    @ToJson
    fun toJson(
      writer: JsonWriter,
      value: ItineraryData?,
      itineraryAdapter: JsonAdapter&lt;Itinerary&gt;
    ) {
      if (value == null) {
        writer.nullValue()
        return
      }
      writer.beginObject()
      writer.name(&quot;itinerary_data&quot;)
      writer.beginObject()
      for (i in value.itineraries.indices) {
        val itinerary = value.itineraries[i]
        writer.name(&quot;itinerary_$i&quot;)
        itineraryAdapter.toJson(writer, itinerary)
      }
      writer.endObject()
      writer.endObject()
    }

    private val options = JsonReader.Options.of(
      &quot;itinerary_data&quot;
    )
  }
}

@JsonClass(generateAdapter = true)
class Itinerary {
}

huangapple
  • 本文由 发表于 2023年2月19日 21:57:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/75500629.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定