Я пытаюсь прочитать следующие данные из API:
[
[
1724180400000,
"59191.45000000",
"59597.28000000",
"59137.78000000",
"59511.99000000",
"544.56764000",
1724183999999,
"32351755.73378980",
51002,
"290.20494000",
"17241827.16012430",
"0"
],
[
1724184000000,
"59512.00000000",
"59722.72000000",
"59421.38000000",
"59589.07000000",
"301.58055000",
1724187599999,
"17968010.37382020",
23563,
"169.48375000",
"10099057.30721750",
"0"
]
]
Но Retrofit ожидает, что ответ JSON будет начинаться с «{» вместо «[».
Я попробовал сериализовать данные со следующими зависимостями:
implementation(libs.retrofit)
implementation(libs.retrofit2.kotlinx.serialization.converter)
implementation(libs.okhttp)
implementation(libs.kotlinx.serialization.json)
Когда я говорю Retrofit ожидать список со списками, он сообщает, что «Сериализатор для класса «Любой» не найден». Как сериализовать этот тип ответа только с помощью массивов, содержащих смешанные типы (String и Int)?
Я просто спрашиваю, как сообщить Retrofit ожидать такого типа ответа. Очевидно, все, что я пробовал до сих пор, было неправильным. Я надеялся прочитать данные в своем сервисе как List<List<any>>.
«Я просто спрашиваю, как сообщить Retrofit ожидать такого типа ответа» — проблема не столько в Retrofit, сколько в сериализации Kotlin, преобразователе, который вы используете. Модернизация не требуется, за исключением делегирования преобразования JSON в типы сериализации Kotlin. Итак, вам нужно определить, как сериализация Kotlin обрабатывает списки разнородных типов. Мне не пришлось беспокоиться об этом (структура JSON не самая лучшая...), но я бы изучил этот вариант.
Если тип не имеет значения, вы можете просто использовать тип String
.
@GET("test")
fun test(): Call<List<List<String>>>
Если тип имеет значение, вы можете использовать JsonPrimitive
.
@GET("test")
fun test(): Call<List<List<JsonPrimitive>>>
После того, как вы получили данные, вы можете проанализировать их List<List<Any>>
вот так.
val result: List<List<JsonPrimitive>> = response.body()!!
val parsedResult: List<List<Any>> = result.map { innerItem ->
innerItem.map {
if (it.isNumber) it.asInt
else it.asString
}
}
println(parsedResult)
Этот ответ великолепен! Я потратил часы, пока мне просто нужно было ввести внутренний список как JsonPrimitive. Поскольку сетевые вызовы находятся внутри функции приостановки, тип вызова в моем сценарии не требуется. Но можете ли вы объяснить мне, что такое JsonPrimitive и когда мне его использовать? Неужели только заменить Any, чтобы получить хотя бы исчерпывающий набор возможных типов?
@Marc JSON имеет 6 типов данных. Среди них String, Number (включая Int, Long, Double и т. д.) и Boolean считаются примитивами JSON. Итак, вы можете использовать JsonPrimitive
, если ваш ответ API содержит смешанные примитивные типы данных. Но если у вас есть API, обычно рекомендуется избегать использования JsonPrimitive, поскольку API должен поддерживать согласованные типы данных (не смешанные).
Это не мой API. Я думаю, что данные форматируются таким образом, потому что они обслуживают большие объемы данных и хотят, чтобы размер сетевых данных был как можно меньшим. Но мне как потребителю это совсем не удобно... В любом случае Ваш ответ очень помог в решении вопроса! Я буду использовать JsonPrimitive
в своих приложениях для Android, чтобы обрабатывать подобные ответы.
Ответы в этой теме очень помогли понять и решить проблему. В своем приложении для Android я реализовал такое решение:
В сервисе API (интерфейс Retrofit 2)
@GET("klines.json")
suspend fun getKlines(): List<List<JsonPrimitive>>
В сетевом репозитории извлеките и преобразуйте rawJson в мой собственный класс Kline.
override suspend fun getKlines(): List<Kline> {
val rawJson = apiService.getKlines()
return rawJson.map { data ->
Kline(
timestamp = data[0].long,
open = data[1].double,
high = data[2].double,
low = data[3].double,
close = data[4].double,
volume = data[5].double,
closeTime = data[6].long,
quoteAssetVolume = data[7].double,
numberOfTrades = data[8].int,
takerBuyBaseAssetVolume = data[9].double,
takerBuyQuoteAssetVolume = data[10].double
)
}
}
Тип Call
в моем сценарии не понадобился, поскольку функции вызываются из области сопрограммы.
Результат rawJson в logcat:
[[1724151600000, "60716.01000000", "60734.91000000", "60401.90000000", "60518.82000000", "1086.55870000", 1724155199999, "65807817.60717790", 58639, "538.35193000", "32602791.90598670", "0"], [...] ]
Эта реализация работает для меня, но приветствуются лучшие решения!
«Но Retrofit ожидает, что ответ JSON будет начинаться с '{' вместо '['». -- Дооснащение ничего не предполагает. Вы сообщили Retrofit, чего ожидать, и поэтому возникла проблема с тем, что у вас есть в вашем API Retrofit (например, функция с аннотацией
@GET
). Я рекомендую вам отредактировать свой вопрос и предоставить эту функцию вместе с объявлением класса о том, что эта функция возвращает, чтобы мы могли сравнить и сопоставить ее с JSON, который вы ожидаете.