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 currently
dalam ["currently"]!["temperature"]
) dan karena Anda menggunakan tipe kumpulan Foundation seperti NSDictionary
kompilator 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 URLSession
dan 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 currentConditions
Anda 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 .mutableContainers
atau .mutableLeaves
opsi yang benar-benar tidak masuk akal di Swift. Kedua opsi tersebut adalah opsi Objective-C warisan untuk menetapkan hasil ke NSMutable...
objek. Di Swift, var
iable apa pun dapat berubah secara default dan meneruskan salah satu opsi tersebut dan menetapkan hasilnya ke let
konstanta 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 .allowFragments
yang diperlukan jika jika JSON akar objek bisa menjadi jenis nilai ( String
, Number
, Bool
atau null
) bukan salah satu jenis koleksi ( array
atau dictionary
). Tetapi biasanya menghilangkan options
parameter 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
123
atau 123.0
- Swift: Int
atauDouble
- Bool - JSON:
true
atau false
tidak dalam tanda kutip ganda - Swift: true
ataufalse
- 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 ( OneOfSupportedJSONTypes
baik 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 .allowFragments
opsi 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+, Codable
protokol 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
URL
dapat diterjemahkan secara langsung sebagai URL
.
- The
time
integer dapat diterjemahkan sebagai Date
dengan 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: