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ư currently
trong ["currently"]!["temperature"]
) và vì bạn đang sử dụng các loại bộ sưu tập Foundation như NSDictionary
trì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 URLSession
và độ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ị currentConditions
bạ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 .mutableContainers
hoặc .mutableLeaves
cá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ỳ var
iable 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 let
hằ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
, Bool
hoặc null
) chứ không phải là một trong những loại bộ sưu tập ( array
hoặc dictionary
). Nhưng thông thường bỏ qua options
tham 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
123
hoặc 123.0
- Swift: Int
hoặcDouble
- Bool - JSON:
true
hoặc false
không trong dấu ngoặc kép - Swift: true
hoặ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 ( OneOfSupportedJSONTypes
là 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 .allowFragments
tù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+, Codable
giao 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
URL
có thể được giải mã trực tiếp như URL
.
- Số
time
nguyên có thể được giải mã như Date
vớ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: