Как читать массив массивов JSON со смешанными значениями в Retrofit 2?

Я пытаюсь прочитать следующие данные из 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 ожидает, что ответ JSON будет начинаться с '{' вместо '['». -- Дооснащение ничего не предполагает. Вы сообщили Retrofit, чего ожидать, и поэтому возникла проблема с тем, что у вас есть в вашем API Retrofit (например, функция с аннотацией @GET). Я рекомендую вам отредактировать свой вопрос и предоставить эту функцию вместе с объявлением класса о том, что эта функция возвращает, чтобы мы могли сравнить и сопоставить ее с JSON, который вы ожидаете.

CommonsWare 20.08.2024 22:43

Я просто спрашиваю, как сообщить Retrofit ожидать такого типа ответа. Очевидно, все, что я пробовал до сих пор, было неправильным. Я надеялся прочитать данные в своем сервисе как List<List<any>>.

Marc 20.08.2024 22:49

«Я просто спрашиваю, как сообщить Retrofit ожидать такого типа ответа» — проблема не столько в Retrofit, сколько в сериализации Kotlin, преобразователе, который вы используете. Модернизация не требуется, за исключением делегирования преобразования JSON в типы сериализации Kotlin. Итак, вам нужно определить, как сериализация Kotlin обрабатывает списки разнородных типов. Мне не пришлось беспокоиться об этом (структура JSON не самая лучшая...), но я бы изучил этот вариант.

CommonsWare 20.08.2024 22:55
0
3
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Если тип не имеет значения, вы можете просто использовать тип 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 21.08.2024 12:45

@Marc JSON имеет 6 типов данных. Среди них String, Number (включая Int, Long, Double и т. д.) и Boolean считаются примитивами JSON. Итак, вы можете использовать JsonPrimitive, если ваш ответ API содержит смешанные примитивные типы данных. Но если у вас есть API, обычно рекомендуется избегать использования JsonPrimitive, поскольку API должен поддерживать согласованные типы данных (не смешанные).

Kaung Khant Kyaw 21.08.2024 13:05

Это не мой API. Я думаю, что данные форматируются таким образом, потому что они обслуживают большие объемы данных и хотят, чтобы размер сетевых данных был как можно меньшим. Но мне как потребителю это совсем не удобно... В любом случае Ваш ответ очень помог в решении вопроса! Я буду использовать JsonPrimitive в своих приложениях для Android, чтобы обрабатывать подобные ответы.

Marc 21.08.2024 13:25

Ответы в этой теме очень помогли понять и решить проблему. В своем приложении для 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"], [...] ] 

Эта реализация работает для меня, но приветствуются лучшие решения!

Другие вопросы по теме