Pertama-tama jangan pernah memuat data secara sinkron dari URL jarak jauh , gunakan selalu metode asinkron seperti URLSession.
'Any' tidak memiliki anggota subskrip
terjadi karena kompilator tidak tahu jenis objek perantara (misalnya currentlydalam ["currently"]!["temperature"]) dan karena Anda menggunakan tipe kumpulan Foundation seperti NSDictionarykompilator tidak tahu sama sekali tentang tipe tersebut.
Selain itu di Swift 3, Anda perlu memberi tahu kompiler tentang tipe semua objek yang disubscribe.
Anda harus mentransmisikan hasil serialisasi JSON ke tipe sebenarnya.
Kode ini menggunakan URLSessiondan tipe asli Swift secara eksklusif
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()
Untuk mencetak semua pasangan kunci / nilai currentConditionsAnda dapat menulis
let currentConditions = parsedData["currently"] as! [String:Any]
for (key, value) in currentConditions {
print("\(key) - \(value) ")
}
Catatan tentang jsonObject(with data:
Banyak (sepertinya semua) tutorial menyarankan .mutableContainersatau .mutableLeavesopsi yang benar-benar tidak masuk akal di Swift. Kedua opsi tersebut adalah opsi Objective-C warisan untuk menetapkan hasil ke NSMutable...objek. Di Swift, variable apa pun dapat berubah secara default dan meneruskan salah satu opsi tersebut dan menetapkan hasilnya ke letkonstanta tidak berpengaruh sama sekali. Lebih jauh lagi, sebagian besar implementasi tidak pernah memutasi JSON yang telah dideserialisasi.
Satu-satunya (jarang) pilihan yang berguna di Swift adalah .allowFragmentsyang diperlukan jika jika JSON akar objek bisa menjadi jenis nilai ( String, Number, Boolatau null) bukan salah satu jenis koleksi ( arrayatau dictionary). Tetapi biasanya menghilangkan optionsparameter yang berarti Tidak ada opsi .
================================================== =========================
Beberapa pertimbangan umum untuk mengurai JSON
JSON adalah format teks yang tersusun rapi. Sangat mudah untuk membaca string JSON. Baca string dengan cermat . Hanya ada enam tipe berbeda - dua tipe koleksi dan empat tipe nilai.
Jenis koleksinya adalah
- Array - JSON: objek dalam tanda kurung siku
[]- Swift: [Any]tetapi dalam banyak kasus[[String:Any]]
- Kamus - JSON: objek dalam kurung kurawal
{}- Swift:[String:Any]
Jenis nilainya adalah
- String - JSON: nilai apa pun dalam tanda kutip ganda
"Foo", genap "123"atau "false"- Swift:String
- Angka - JSON: nilai numerik tidak dalam tanda kutip ganda
123atau 123.0- Swift: IntatauDouble
- Bool - JSON:
trueatau false tidak dalam tanda kutip ganda - Swift: trueataufalse
- null - JSON:
null- Swift:NSNull
Menurut spesifikasi JSON, semua kunci dalam kamus harus ada String.
Pada dasarnya, selalu disarankan untuk menggunakan binding opsional untuk membuka bungkus opsional dengan aman
Jika objek root adalah dictionary ( {}), gunakan tipe tersebut[String:Any]
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [String:Any] { ...
dan mengambil nilai dengan kunci dengan ( OneOfSupportedJSONTypesbaik koleksi JSON atau tipe nilai seperti yang dijelaskan di atas.)
if let foo = parsedData["foo"] as? OneOfSupportedJSONTypes {
print(foo)
}
Jika objek root adalah array ( []) gunakan tipe[[String:Any]]
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]] { ...
dan melakukan iterasi melalui array dengan
for item in parsedData {
print(item)
}
Jika Anda membutuhkan item pada indeks tertentu, periksa juga apakah indeks tersebut ada
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]], parsedData.count > 2,
let item = parsedData[2] as? OneOfSupportedJSONTypes {
print(item)
}
}
Dalam kasus yang jarang terjadi, JSON hanyalah salah satu jenis nilai - bukan jenis koleksi - Anda harus meneruskan .allowFragmentsopsi dan mentransmisikan hasilnya ke jenis nilai yang sesuai, misalnya
if let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? String { ...
Apple telah menerbitkan artikel komprehensif di Swift Blog: Working with JSON in Swift
================================================== =========================
Di Swift 4+, Codableprotokol menyediakan cara yang lebih mudah untuk mengurai JSON langsung ke dalam struct / kelas.
Misalnya contoh JSON yang diberikan dalam pertanyaan (sedikit diubah)
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}
"""
dapat diterjemahkan ke dalam struct Weather. Jenis Swift sama seperti yang dijelaskan di atas. Ada beberapa opsi tambahan:
- String yang merepresentasikan sebuah
URLdapat diterjemahkan secara langsung sebagai URL.
- The
timeinteger dapat diterjemahkan sebagai Datedengan dateDecodingStrategy .secondsSince1970.
- kunci JSON snaked_cased dapat diubah menjadi camelCase dengan
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)
}
Sumber Berkode lainnya: