Trước hết không bao giờ tải dữ liệu đồng bộ từ một URL từ xa , hãy luôn sử dụng các phương thức không đồng bộ như thế nào URLSession.
'Bất kỳ' không có thành viên đăng ký
xảy ra bởi vì trình biên dịch không có ý tưởng về loại đối tượng trung gian (ví dụ như currentlytrong ["currently"]!["temperature"]) và vì bạn đang sử dụng các loại bộ sưu tập Foundation như NSDictionarytrình biên dịch hoàn toàn không biết gì về loại.
Ngoài ra, trong Swift 3, cần phải thông báo cho trình biên dịch về loại của tất cả các đối tượng được đăng ký.
Bạn phải chuyển kết quả của tuần tự hóa JSON thành loại thực tế.
Mã này sử dụng URLSessionvà độc quyền các loại bản địa Swift
let urlString = "https://api.forecast.io/forecast/apiKey/37.5673776,122.048951"
let url = URL(string: urlString)
URLSession.shared.dataTask(with:url!) { (data, response, error) in
if error != nil {
print(error)
} else {
do {
let parsedData = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
let currentConditions = parsedData["currently"] as! [String:Any]
print(currentConditions)
let currentTemperatureF = currentConditions["temperature"] as! Double
print(currentTemperatureF)
} catch let error as NSError {
print(error)
}
}
}.resume()
Để in tất cả các cặp khóa / giá trị currentConditionsbạn có thể viết
let currentConditions = parsedData["currently"] as! [String:Any]
for (key, value) in currentConditions {
print("\(key) - \(value) ")
}
Một lưu ý liên quan đến jsonObject(with data:
Nhiều hướng dẫn (có vẻ như tất cả) đề xuất .mutableContainershoặc .mutableLeavescác tùy chọn hoàn toàn vô nghĩa trong Swift. Hai tùy chọn là các tùy chọn Objective-C kế thừa để gán kết quả cho NSMutable...các đối tượng. Trong Swift, bất kỳ variable nào cũng có thể thay đổi theo mặc định và chuyển bất kỳ tùy chọn nào trong số đó và gán kết quả cho lethằng số không có tác dụng gì cả. Hơn nữa, hầu hết các triển khai đều không bao giờ làm thay đổi JSON được giải trừ.
Các chỉ (hiếm) tùy chọn đó là hữu ích trong Swift là .allowFragmentsđó là cần thiết nếu nếu đối tượng gốc JSON có thể là một kiểu giá trị ( String, Number, Boolhoặc null) chứ không phải là một trong những loại bộ sưu tập ( arrayhoặc dictionary). Nhưng thông thường bỏ qua optionstham số có nghĩa là Không có tùy chọn .
================================================== =========================
Một số cân nhắc chung để phân tích JSON
JSON là một định dạng văn bản được sắp xếp tốt. Thật dễ dàng để đọc một chuỗi JSON. Đọc chuỗi cẩn thận . Chỉ có sáu loại khác nhau - hai loại bộ sưu tập và bốn loại giá trị.
Các loại bộ sưu tập là
- Mảng - JSON: các đối tượng trong ngoặc vuông
[]- Swift: [Any]nhưng trong hầu hết các trường hợp[[String:Any]]
- Từ điển - JSON: các đối tượng trong dấu ngoặc nhọn
{}- Swift:[String:Any]
Các loại giá trị là
- Chuỗi - JSON: bất kỳ giá trị nào trong dấu ngoặc kép
"Foo", chẵn "123"hoặc "false"- Swift:String
- Số - JSON: giá trị số không nằm trong dấu ngoặc kép
123hoặc 123.0- Swift: InthoặcDouble
- Bool - JSON:
truehoặc false không trong dấu ngoặc kép - Swift: truehoặcfalse
- null - JSON:
null- Swift:NSNull
Theo đặc tả JSON, tất cả các khóa trong từ điển đều phải có String.
Về cơ bản, nó luôn được khuyến nghị sử dụng các ràng buộc tùy chọn cho các tùy chọn mở khóa một cách an toàn
Nếu đối tượng gốc là một từ điển ( {}) truyền kiểu[String:Any]
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [String:Any] { ...
và truy xuất các giá trị bằng các khóa bằng ( OneOfSupportedJSONTypeslà bộ sưu tập JSON hoặc loại giá trị như được mô tả ở trên.)
if let foo = parsedData["foo"] as? OneOfSupportedJSONTypes {
print(foo)
}
Nếu đối tượng gốc là một mảng ( []) truyền kiểu[[String:Any]]
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]] { ...
và lặp qua mảng với
for item in parsedData {
print(item)
}
Nếu bạn cần một mục tại chỉ mục cụ thể, hãy kiểm tra nếu chỉ mục tồn tại
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]], parsedData.count > 2,
let item = parsedData[2] as? OneOfSupportedJSONTypes {
print(item)
}
}
Trong trường hợp hiếm hoi, JSON chỉ đơn giản là một trong các loại giá trị - chứ không phải là loại bộ sưu tập - bạn phải chuyển .allowFragmentstùy chọn và chuyển kết quả sang loại giá trị phù hợp chẳng hạn
if let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? String { ...
Apple đã xuất bản một bài viết toàn diện trên Blog Swift: Làm việc với JSON trong Swift
================================================== =========================
Trong Swift 4+, Codablegiao thức cung cấp một cách thuận tiện hơn để phân tích JSON trực tiếp thành các cấu trúc / lớp.
Ví dụ: mẫu JSON đã cho trong câu hỏi (được sửa đổi một chút)
let jsonString = """
{"icon": "partly-cloudy-night", "precipProbability": 0, "pressure": 1015.39, "humidity": 0.75, "precip_intensity": 0, "wind_speed": 6.04, "summary": "Partly Cloudy", "ozone": 321.13, "temperature": 49.45, "dew_point": 41.75, "apparent_temperature": 47, "wind_bearing": 332, "cloud_cover": 0.28, "time": 1480846460}
"""
có thể được giải mã thành struct Weather. Các loại Swift giống như mô tả ở trên. Có một vài lựa chọn bổ sung:
- Chuỗi đại diện cho một
URLcó thể được giải mã trực tiếp như URL.
- Số
timenguyên có thể được giải mã như Datevới dateDecodingStrategy .secondsSince1970.
- Các khóa JSON của snaken_casing có thể được chuyển đổi thành camelCase với
keyDecodingStrategy .convertFromSnakeCase
struct Weather: Decodable {
let icon, summary: String
let pressure: Double, humidity, windSpeed : Double
let ozone, temperature, dewPoint, cloudCover: Double
let precipProbability, precipIntensity, apparentTemperature, windBearing : Int
let time: Date
}
let data = Data(jsonString.utf8)
do {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode(Weather.self, from: data)
print(result)
} catch {
print(error)
}
Các nguồn có thể mã hóa khác: