Парсинг CoinMarketCap API с использованием декодируемого протокола Swift - PullRequest
0 голосов
/ 20 мая 2018

Я прочитал Как декодировать вложенную структуру JSON с протоколом Swift Decodable? Это не относится к моему конкретному случаю использования, когда в качестве корневых словарей используются строковые литералы.

Также Как декодировать вложенную структуру JSON по протоколу Swift Decodable? ответ от Imanou Petit. Невозможно декодировать данные JSON из API. ответ от Лео Дабуса.

Валюты - это сами словари, представленные литеральными строковыми числами внутри словаря data, так что это отбрасывает меня.Я ищу самую модель Swifty 4, использующую перечисления, где легко увидеть, какие контейнеры соответствуют каким словарям.

ps Дэвид Берри дал отличный ответ, который я реализовал ниже.Если у кого-то есть другие подходы к тому же результату, я бы хотел увидеть другие предложения.Возможно, есть более новые методы Swift 4, которые еще не известны или другие шаблоны проектирования.

Код

struct RawServerResponse {

    enum RootKeys: String, CodingKey {
        case data
        case btc = "1"
        case eth = "1027"
        case iota = "1720"
        case ripple = "52"
        case neo = "1376"
        case quotes
        case USD
    }

    enum BaseKeys: String, CodingKey {
        case id, name, symbol, maxSupply = "max_supply"
    }

    enum QuotesKeys: String, CodingKey {
        case USD
    }

    enum USDKeys: String, CodingKey {
        case price, marketCap = "market_cap"
    }

    let data: String
    let id: Int
    let name: String
    let symbol: String
    let maxSupply: Double
    let price: Double
    let marketCap: Double
}

extension RawServerResponse: Decodable {

    init(from decoder: Decoder) throws {
        // data
        let container = try decoder.container(keyedBy: RootKeys.self)
        data = try container.decode(String.self, forKey: .data)

        // id
        let idContainer = try container.nestedContainer(keyedBy: BaseKeys.self, forKey: .data)
        id = try idContainer.decode(Int.self, forKey: .id) 

        // price
        let priceContainer = try container.nestedContainer(keyedBy: USDKeys.self, forKey: .USD)
        price = try priceContainer.decode(Double.self, forKey: .price)

    }

}

API / JSON https://api.coinmarketcap.com/v2/ticker/

{
"data": {
    "1": {
        "id": 1, 
        "name": "Bitcoin", 
        "symbol": "BTC", 
        "website_slug": "bitcoin", 
        "rank": 1, 
        "circulating_supply": 17041575.0, 
        "total_supply": 17041575.0, 
        "max_supply": 21000000.0, 
        "quotes": {
            "USD": {
                "price": 8214.7, 
                "volume_24h": 5473430000.0, 
                "market_cap": 139991426153.0, 
                "percent_change_1h": 0.09, 
                "percent_change_24h": 2.29, 
                "percent_change_7d": -2.44
            }
        }, 
        "last_updated": 1526699671
    }, 
    "1027": {
        "id": 1027, 
        "name": "Ethereum", 
        "symbol": "ETH", 
        "website_slug": "ethereum", 
        "rank": 2, 
        "circulating_supply": 99524121.0, 
        "total_supply": 99524121.0, 
        "max_supply": null, 
        "quotes": {
            "USD": {
                "price": 689.891, 
                "volume_24h": 2166100000.0, 
                "market_cap": 68660795252.0, 
                "percent_change_1h": 0.13, 
                "percent_change_24h": 2.51, 
                "percent_change_7d": 2.54
            }
        }, 
        "last_updated": 1526699662
    }
}

1 Ответ

0 голосов
/ 20 мая 2018

Я бы взял более простой подход к данным, рассматривал «данные» как набор идентичных ответов с ключами, аналогично, «цитаты» - это набор цитат с ключами.

struct RawServerResponse : Decodable {
    enum Keys : String, CodingKey {
        case data = "data"
    }

    let data : [String:Base]
}

struct Base : Decodable {
    enum CodingKeys : String, CodingKey {
        case id = "id"
        case name = "name"
        case symbol = "symbol"
        case maxSupply = "max_supply"
        case quotes = "quotes"
    }

    let id : Int64
    let name : String
    let symbol : String
    let maxSupply : Double?
    let quotes : [String:Quote]
}

struct Quote : Decodable {
    enum CodingKeys : String, CodingKey {
        case price = "price"
        case marketCap = "market_cap"
    }

    let price :  Double
    let marketCap : Double
}

Затем, если вам действительно нужен доступ к отдельным ключевым значениям из этих более простых структур, вы можете предоставить вычисленные средства доступа:

extension RawServerResponse {
    enum BaseKeys : String {
        case btc = "1"
        case eth = "1027"
    }

    var eth : Base? { return data[BaseKeys.eth.rawValue] }
    var btc : Base? { return data[BaseKeys.btc.rawValue] }
}

И, аналогично, вы можете создать аналогичные средства доступа для валют:

extension Base {
    enum Currencies : String {
        case usd = "USD"
    }

    var usd : Quote? { return quotes[Currencies.usd.rawValue]}
}

Как только вы уладите эту часть, тогда эта ссылка из вашего исходного вопроса покажет вам, как сгладить структуру, если вы этого хотите.По сути, это сводится к изменению вычисляемых свойств на let свойства, которые вы задали бы как часть конструктора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...