Разбор JSON, проблема со вложенной структурой JSON - PullRequest
0 голосов
/ 13 июня 2018

Допустим, у нас есть URL , я использую CodableAlamofire для получения и анализа ответа JSON.

Для указанного выше ответа URL, который я создалследующие классы Codable.

struct CoinList: Codable {
    let raw: Raw
    let display: Display

    enum CodingKeys: String, CodingKey {
        case raw = "RAW"
        case display = "DISPLAY"
    }
}

struct Display: Codable {
    let usd: [String: Usd]

    enum CodingKeys: String, CodingKey {
        case usd = "USD"
    }
}

struct Raw: Codable {
    let usd: [String: Usd]

    enum CodingKeys: String, CodingKey {
        case usd = "USD"
    }
}

struct Usd: Codable {
    let type, market, fromsymbol, tosymbol: String
    let flags: String
    let price: Double
    let lastupdate: Int
    let lastvolume, lastvolumeto: Double
    let lasttradeid: String
    let volumeday, volumedayto, volume24Hour, volume24Hourto: Double
    let openday, highday, lowday, open24Hour: Double
    let high24Hour, low24Hour: Double
    let lastmarket: String
    let change24Hour, changepct24Hour, changeday, changepctday: Double
    let supply, mktcap, totalvolume24H, totalvolume24Hto: Double

    enum CodingKeys: String, CodingKey {
        case type = "TYPE"
        case market = "MARKET"
        case fromsymbol = "FROMSYMBOL"
        case tosymbol = "TOSYMBOL"
        case flags = "FLAGS"
        case price = "PRICE"
        case lastupdate = "LASTUPDATE"
        case lastvolume = "LASTVOLUME"
        case lastvolumeto = "LASTVOLUMETO"
        case lasttradeid = "LASTTRADEID"
        case volumeday = "VOLUMEDAY"
        case volumedayto = "VOLUMEDAYTO"
        case volume24Hour = "VOLUME24HOUR"
        case volume24Hourto = "VOLUME24HOURTO"
        case openday = "OPENDAY"
        case highday = "HIGHDAY"
        case lowday = "LOWDAY"
        case open24Hour = "OPEN24HOUR"
        case high24Hour = "HIGH24HOUR"
        case low24Hour = "LOW24HOUR"
        case lastmarket = "LASTMARKET"
        case change24Hour = "CHANGE24HOUR"
        case changepct24Hour = "CHANGEPCT24HOUR"
        case changeday = "CHANGEDAY"
        case changepctday = "CHANGEPCTDAY"
        case supply = "SUPPLY"
        case mktcap = "MKTCAP"
        case totalvolume24H = "TOTALVOLUME24H"
        case totalvolume24Hto = "TOTALVOLUME24HTO"
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)

        type = try values.decode(String.self, forKey: .type)
        market = try values.decode(String.self, forKey: .market)
        fromsymbol = try values.decode(String.self, forKey: .fromsymbol)
        tosymbol = try values.decode(String.self, forKey: .tosymbol)
        flags = try values.decode(String.self, forKey: .flags)

        price = try values.decode(Double.self, forKey: .price)
        lastvolume = try values.decode(Double.self, forKey: .lastvolume)
        lastvolumeto = try values.decode(Double.self, forKey: .lastvolumeto)
        lastupdate = try values.decode(Int.self, forKey: .lastupdate)

        if let value = try? values.decode(Int.self, forKey: .lasttradeid) {
            lasttradeid = String(value)
        } else {
            lasttradeid = try values.decode(String.self, forKey: .lasttradeid)
        }

        volumeday = try values.decode(Double.self, forKey: .volumeday)
        volumedayto = try values.decode(Double.self, forKey: .volumedayto)
        volume24Hour = try values.decode(Double.self, forKey: .volume24Hour)
        volume24Hourto = try values.decode(Double.self, forKey: .volume24Hourto)
        openday = try values.decode(Double.self, forKey: .openday)
        highday = try values.decode(Double.self, forKey: .highday)
        lowday = try values.decode(Double.self, forKey: .lowday)
        open24Hour = try values.decode(Double.self, forKey: .open24Hour)
        high24Hour = try values.decode(Double.self, forKey: .high24Hour)
        low24Hour = try values.decode(Double.self, forKey: .low24Hour)

        lastmarket = try values.decode(String.self, forKey: .lastmarket)

        change24Hour = try values.decode(Double.self, forKey: .change24Hour)
        changepct24Hour = try values.decode(Double.self, forKey: .changepct24Hour)
        changeday = try values.decode(Double.self, forKey: .changeday)
        changepctday = try values.decode(Double.self, forKey: .changepctday)

        supply = try values.decode(Double.self, forKey: .supply)
        mktcap = try values.decode(Double.self, forKey: .mktcap)
        totalvolume24H = try values.decode(Double.self, forKey: .totalvolume24H)
        totalvolume24Hto = try values.decode(Double.self, forKey: .totalvolume24Hto)
    }
}

После успешного ответа я не могу разобрать JSON, я много изучал вложенный анализ JSON с помощью Swift Codable, но все равно не смог добиться успеха.

Пожалуйста, помогите мне разобрать приведенный выше ответ JSON с вложенной структурой JSON, например, например, Отображение и Необработанный объект имеет все свойства Usd .

Я думаю, что я совершил небольшую ошибку.

Буду признателен за любую помощь.

ОБНОВЛЕНИЕ

Iсоздал файл JSON для ответа и проанализировал его,

if let path = Bundle.main.path(forResource: "test", ofType: "json") {
    do {
        let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
        let result = try JSONDecoder().decode(CoinList.self, from: data)
        print(result)
    } catch {
        print(error.localizedDescription)
    }
}

Ошибка заключается в следующем:

enter image description here

Структура JSON похожаэто,

enter image description here

Пожалуйста, найдите мой мой веб-вызов API,

    Alamofire.request(TickerRouter.PriceMultiFull(params: params))
        .validate(statusCode: 200..<300)
        .responseString { responseData in                
            let data = responseData.value?.replacingOccurrences(of: "\\/", with: "/").data(using: .utf8)
            if responseData.error == nil {
                let decoder = JSONDecoder()
                decoder.keyDecodingStrategy = .custom({ AnyKey(stringValue: $0.last!.stringValue.lowercased())!})
                decoder.dateDecodingStrategy = .secondsSince1970
                let result = try? decoder.decode(CoinList.self, from: data!)
                success(result!)
            } else {
                let msg = "Something went wrong. Please try again later"
                failure(msg)
            }
    }

результат равен нулю, он работает с местнымJSON файл.(

1 Ответ

0 голосов
/ 13 июня 2018

На первый взгляд вы заметите, что типы элементов в словаре USD для raw и display сильно различаются, поэтому одна структура для обоих не работает.

Кореньобъект (ключи String - это символы BTH и XRP)

struct CoinList: Codable {
    let raw: [String: Raw]
    let display: [String: Display]
}

Структуры Raw и Display содержат ключ usd и соответствующую структуру

struct Raw: Codable {
    let usd: USDRaw
}

struct Display: Codable {
    let usd: USDDisplay
}

Структуры USDRaw и USDDisplay содержат все данные, lastupdate in USDRaw будет декодироваться как Date

struct USDRaw: Codable {
    let type, market, flags, fromsymbol, tosymbol: String
    let price : Double
    let lastupdate: Date
    let lastvolume, lastvolumeto: Double
    let lasttradeid: String
    let volumeday, volumedayto, volume24hour, volume24hourto: Double
    let openday, highday, lowday, open24hour: Double
    let high24hour, low24hour: Double
    let lastmarket: String
    let change24hour, changepct24hour, changeday, changepctday: Double
    let supply, mktcap, totalvolume24h, totalvolume24hto: Double

}

struct USDDisplay: Codable {
    let fromsymbol, tosymbol, market, price, lastupdate: String
    let lastvolume, lastvolumeto, lasttradeid, volumeday, volumedayto, volume24hour, volume24hourto : String
    let openday, highday, lowday, open24hour, high24hour, low24hour, lastmarket: String
    let change24hour, changepct24hour, changeday, changepctday: String
    let supply, mktcap, totalvolume24h, totalvolume24hto: String
}

Чтобы избавиться от указаниявсе CodingKey и сделать ключи в нижнем регистре создать вспомогательную структуру ( украдено из документации )

struct AnyKey: CodingKey {
    var stringValue: String
    var intValue: Int?

    init?(stringValue: String) {
        self.stringValue = stringValue
        self.intValue = nil
    }

    init?(intValue: Int) {
        self.stringValue = String(intValue)
        self.intValue = intValue
    }
}

Передать пользовательский keyDecodingStrategy и подходящий dateDecodingStrategyк декодеру

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .custom({ AnyKey(stringValue: $0.last!.stringValue.lowercased())!}) 
decoder.dateDecodingStrategy = .secondsSince1970
let coinList = try decoder.decode(CoinList.self, from: data)
print(coinList)
...