Как декодировать [Строка: Любая], используя Арго
Я только что изучил основы Argo и смог расшифровать 99% моих JSON-файлов. Теперь я сталкиваюсь со следующей структурой (ключи типа "5447" и "5954" являются динамическими) и мне нужна помощь:
{
"5447": {
"business_id": 5447,
"rating": 5,
"comment": "abcd",
"replies_count": 0,
"message_id": 2517
},
"5954": {
"business_id": 5954,
"rating": 3,
"comment": "efgh",
"replies_count": 0,
"message_id": 633
}
}
Типичный пример декодирования Арго выглядит так:
struct User {
let id: Int
let name: String
}
для структуры JSON (ключи фиксированы "id" и "name"):
{
"id": 124,
"name": "someone"
}
используя что-то вроде этого:
extension User: Decodable {
static func decode(j: JSON) -> Decoded<User> {
return curry(User.init)
<^> j <| "id"
<*> j <| "name"
}
}
Однако структура данных, которую мне нужно проанализировать, не подходит к примеру.
ОБНОВЛЕНИЕ: используя первую реализацию Тони с небольшой модификацией в последней строке, я выполнил свою работу. Вот полный рабочий код:
Business.swift:
import Argo
import Curry
import Runes
struct Business {
let businessID: Int
let rating: Double?
let comment: String?
let repliesCount: Int?
let messageID: Int?
}
extension Business: Decodable {
static func decode(_ json: JSON) -> Decoded<Business> {
let c0 = curry(Business.init)
<^> json <| "business_id"
<*> json <|? "rating"
return c0
<*> json <|? "comment"
<*> json <|? "replies_count"
<*> json <|? "message_id"
}
}
Businesses.swift
import Argo
import Runes
struct Businesses {
let businesses: [Business]
}
extension Businesses: Decodable {
static func decode(_ json: JSON) -> Decoded<Businesses> {
let dict = [String: JSON].decode(json)
let arr = dict.map { Array($0.map { $1 }) }
let jsonArr = arr.map { JSON.array($0) }
return Businesses.init <^> jsonArr.map([Business].decode).value ?? .success([])
}
}
1 ответ
Если в словаре есть динамические ключи, вам придется отказаться от удобных операторов, которые предоставляет Argo. Сначала вам нужно получить JSON
элемент объекта затем сопоставьте его самостоятельно. Так как ключи здесь не имеют значения (потому что идентификаторы также находятся во встроенных словарях), на самом деле это будет не так уж плохо. Самый простой способ - создать новую структуру, чтобы обернуть это:
struct Businesses: Decodable {
let businesses: [Business]
static func decode(_ json: JSON) -> Decoded<Businesses> {
// Get dictionary
let dict: Decoded<[String: JSON]> = [String: JSON].decode(json)
// Transform dictionary to array
let array: Decoded<[JSON]> = dict.map { Array($0.map { $1 }) }
// Wrap array back into a JSON type
let jsonArray: Decoded<JSON> = array.map { JSON.array($0) }
// Decode the JSON type like we would with no key
return Businesses.init <^> array.map([Business].decode)
}
}
Здесь мы получаем словарь и преобразуем его в массив, чтобы мы могли декодировать его, как любой другой массив.
Вы также можете пропустить преобразование в часть массива и декодировать его из словаря следующим образом:
static func decode(_ json: JSON) -> Decoded<Businesses> {
// Get dictionary
let dict: Decoded<[String: JSON]> = [String: JSON].decode(json)
// Map over the dictionary and decode the values
let result: Decoded<[Businesses]> = dict.flatMap { object in
let decoded: [Decoded<Business>] = Array(object.map { decode($1) })
return sequence(decoded)
}
return Businesses.init <^> result
}
Я не пробовал ни один из этого кода, поэтому могут быть некоторые изменения. Кроме того, вам, вероятно, не нужны все аннотации типов, но я добавил их, чтобы помочь объяснить код. Вы также можете использовать этот код вне новой структурной модели, в зависимости от вашего приложения.