英文:
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 {
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论