SwiftUI Swift 问题之将 JSON 转换为 Swift 时出现问题“无法读取数据,因为它的格式不正确 The data couldn’t be read because it isn’t in the correct format

实战问题

当我从 coinapi.com ( https://rest-sandbox.coinapi.io/v1/assets/?apikey=72869C8A-D49B-4DC5-9A3B-17D9804AEE97 )转换 JSON 时,我遇到了一个问题“数据不能被阅读,因为它的格式不正确”。

转换后的结果是:

struct CryptListStruct: Codable {

    let assetID, name: String
    let typeIsCrypto: Int
    let dataQuoteStart, dataQuoteEnd, dataOrderbookStart, dataOrderbookEnd: String
    let dataTradeStart, dataTradeEnd: String
    let dataSymbolsCount: Int
    let volume1HrsUsd, volume1DayUsd: Double
    let volume1MthUsd: Int
    let priceUsd: Double?
    let idIcon, dataStart, dataEnd: String

    enum CodingKeys: String, CodingKey {
        case assetID = "asset_id"
        case name
        case typeIsCrypto = "type_is_crypto"
        case dataQuoteStart = "data_quote_start"
        case dataQuoteEnd = "data_quote_end"
        case dataOrderbookStart = "data_orderbook_start"
        case dataOrderbookEnd = "data_orderbook_end"
        case dataTradeStart = "data_trade_start"
        case dataTradeEnd = "data_trade_end"
        case dataSymbolsCount = "data_symbols_count"
        case volume1HrsUsd = "volume_1hrs_usd"
        case volume1DayUsd = "volume_1day_usd"
        case volume1MthUsd = "volume_1mth_usd"
        case priceUsd = "price_usd"
        case idIcon = "id_icon"
        case dataStart = "data_start"
        case dataEnd = "data_end"
    }
}

我认为这是因为那里有很多数据并且它们不一样。我怎样才能得到正确的模型?

这就是我的函数的样子:


    func getCryptList(_ completion: @escaping (CryptListStruct) -> Void, _ error: @escaping (String) -> Void){
    let header: [String: String] = [:]
       self.get(url: "https://rest.coinapi.io/v1/assets/?apikey=367FB27A-371B-4DBD-AB81-E98AAFE857B2", header: header, completion: {
            (data) in
            do {
                guard let data = data else {return}
                let crpytList = try JSONDecoder().decode(CryptListStruct.self, from: data)
                DispatchQueue.main.async {
                    completion(crpytList)
                }
            } catch let err {
                       error(err.localizedDescription)
                   }
               }, error: error)
    }

解决方案

试试这个(注意选项和 volume1MthUsd: Double),对我有用:

struct CryptListStruct: Codable {

    let assetID, name: String
    let typeIsCrypto: Int
    let dataQuoteStart, dataQuoteEnd, dataOrderbookStart, dataOrderbookEnd: String?
    let dataTradeStart, dataTradeEnd: String?
    let dataSymbolsCount: Int
    let volume1HrsUsd, volume1DayUsd, volume1MthUsd: Double
    let priceUsd: Double?
    let idIcon, dataStart, dataEnd: String?

    enum CodingKeys: String, CodingKey {
        case assetID = "asset_id"
        case name
        case typeIsCrypto = "type_is_crypto"
        case dataQuoteStart = "data_quote_start"
        case dataQuoteEnd = "data_quote_end"
        case dataOrderbookStart = "data_orderbook_start"
        case dataOrderbookEnd = "data_orderbook_end"
        case dataTradeStart = "data_trade_start"
        case dataTradeEnd = "data_trade_end"
        case dataSymbolsCount = "data_symbols_count"
        case volume1HrsUsd = "volume_1hrs_usd"
        case volume1DayUsd = "volume_1day_usd"
        case volume1MthUsd = "volume_1mth_usd"
        case priceUsd = "price_usd"
        case idIcon = "id_icon"
        case dataStart = "data_start"
        case dataEnd = "data_end"
    }
}

编辑1:

这就是我测试我的答案的方式:

import SwiftUI

@main
struct TestApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
    struct ContentView: View {
    @State var cryptList = [CryptListStruct]()

    var body: some View {
        Text(cryptList.count > 0 ? "fetched \(cryptList.count)" : "fetching...")
            .task {
                cryptList = await fetchThem()
                print("\n----> cryptList: \(cryptList.count) \n")
            }
        // using closure
        //.onAppear {
        //    getCryptList() { list in
        //        cryptList = list
        //    }
        // }
    }

    private func fetchThem<T: Decodable>() async -> [T] {
        var url = URL(string: "https://rest-sandbox.coinapi.io/v1/assets/?apikey=72869C8A-D49B-4DC5-9A3B-17D9804AEE97")!
        do {
            let (data, response) = try await URLSession.shared.data(for: URLRequest(url: url))
            guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
                print(URLError(.badServerResponse))
                return []
            }
            return try JSONDecoder().decode([T].self, from: data)
        }
        catch {
            return []
        }
    }
}

EDIT2:使用闭合样式:

private func getCryptList(_ completion: @escaping ([CryptListStruct]) -> Void) {
    URLSession.shared.dataTask(with: URL(string: "https://rest-sandbox.coinapi.io/v1/assets/?apikey=72869C8A-D49B-4DC5-9A3B-17D9804AEE97")!,
                               completionHandler: { data, response, error in
        guard let data = data, error == nil else {
            print("error: \(error?.localizedDescription as Optional)")
            return
        }
        do {
            let cryptList = try JSONDecoder().decode([CryptListStruct].self, from: data)
            completion(cryptList)
        }
        catch {
            print(String(describing: error))
        }
    }).resume()
}

精品教程推荐


加入我们一起学习SwiftUI

QQ:3365059189
SwiftUI技术交流QQ群:518696470
教程网站:www.openswiftui.com

发表回复