使用 swift Codable 从 JSON 数组中提取数据

Extracting data from JSON array with swift Codable

我有这样的 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。

这是在 playground 上运行的示例字符串:

   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)
}

I cannot use [Any] here.

解码时永远不要使用 Any JSON 因为通常你知道内容的类型。

要解码数组,您必须使用 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))

此外,还有一个很好的复杂 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]

}