【问题标题】:Extracting data from JSON array with swift Codable使用 swift Codable 从 JSON 数组中提取数据
【发布时间】:2018-06-29 11:30:53
【问题描述】:

我有一个这样的 JSON 响应:

我目前将我的可解码结构设计如下:

    struct PortfolioResponseModel: Decodable {
    var dataset: Dataset

    struct Dataset: Decodable {
        var data: Array<PortfolioData> //I cannot use [Any] here...

        struct PortfolioData: Decodable {
            //how to extract this data ?
        }
    }
   }

问题是,如何提取数组里面的数据,可以是Double或者String。

这是在操场上进行这项工作的示例字符串:

   let myJSONArray =
   """
   {
   "dataset": {
   "data": [
    [
   "2018-01-19",
   181.29
   ],
   [
   "2018-01-18",
   179.8
   ],
   [
   "2018-01-17",
   177.6
   ],
   [
   "2018-01-16",
   178.39
   ]
   ]
   }
   }
   """

提取数据:

do {
    let details2: PortfolioResponseModel = try JSONDecoder().decode(PortfolioResponseModel.self, from: myJSONArray.data(using: .utf8)!)
    //print(details2) 
    //print(details2.dataset.data[0]) //somehow get "2018-01-19"

} catch {
    print(error)
}

【问题讨论】:

    标签: json swift swift4 codable decodable


    【解决方案1】:

    我不能在这里使用 [Any]。

    解码 JSON 时切勿使用Any,因为通常您确实知道内容的类型。

    要解码数组,您必须使用 unkeyedContainer 并按顺序解码值

    struct PortfolioResponseModel: Decodable {
        var dataset: Dataset
    
        struct Dataset: Decodable {
            var data: [PortfolioData]
    
            struct PortfolioData: Decodable {
                let date : String
                let value : Double
    
                init(from decoder: Decoder) throws {
                    var container = try decoder.unkeyedContainer()
                    date = try container.decode(String.self)
                    value = try container.decode(Double.self)
                }
            }
        }
    }
    

    您甚至可以将日期字符串解码为Date

    struct PortfolioData: Decodable {
        let date : Date
        let value : Double
    
        init(from decoder: Decoder) throws {
            var container = try decoder.unkeyedContainer()
            date = try container.decode(Date.self)
            value = try container.decode(Double.self)
        }
    }
    

    如果您向解码器添加日期格式化程序

    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy-MM-dd"
    let decoder = JSONDecoder()
    decoder.dateDecodingStrategy = .formatted(formatter)
    let details2 = try decoder.decode(PortfolioResponseModel.self, from: Data(myJSONArray.utf8))
    

    【讨论】:

    • 那行得通!谢谢。虽然我仍然不明白那个 init(from decoder:) 是如何真正工作的......此外,如果内部数组要包含 5 个项目(3 个字符串和 2 个双精度),这个解决方案是否可以按比例放大),是否可以更新 init 函数来处理这个问题?
    • init(from decoder: ) 是一个自定义初始化程序,您可以在其中提供自己的模式来解码传递的对象。 unkeyedContainer 是解码嵌套数组的唯一方法。当然,解决方案可以按比例放大,只需相应地添加属性和解码行。这些值按出现顺序解码。
    • "这些值按出现的顺序解码..." 酷,谢谢。
    【解决方案2】:

    除此之外,还有一个非常好的例子,可以使用数组进行复杂的 JSON 解析,尤其是 here。我希望这可以帮助那些试图将 Codeable 用于更大、更真实的 JSON 数据的人。

    概述是这样的:假设您有以下 JSON 格式:

    {
    "meta": {
        "page": 1,
        "total_pages": 4,
        "per_page": 10,
        "total_records": 38
    },
    "breweries": [
        {
            "id": 1234,
            "name": "Saint Arnold"
        },
        {
            "id": 52892,
            "name": "Buffalo Bayou"
        }
    ]
    

    }

    这是一种常见的格式,其中嵌套了数组。您可以创建一个封装整个响应的结构,为“breweries”键提供数组,类似于您在上面询问的内容:

    struct PagedBreweries : Codable {
    struct Meta : Codable {
        let page: Int
        let totalPages: Int
        let perPage: Int
        let totalRecords: Int
        enum CodingKeys : String, CodingKey {
            case page
            case totalPages = "total_pages"
            case perPage = "per_page"
            case totalRecords = "total_records"
        }
    }
    
    struct Brewery : Codable {
        let id: Int
        let name: String
    }
    
    let meta: Meta
    let breweries: [Brewery]
    

    }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多