Dengan beberapa inspirasi dari inti yang saya temukan, saya menulis beberapa ekstensi untuk UnkeyedDecodingContainer
dan KeyedDecodingContainer
. Anda dapat menemukan tautan ke inti saya di sini . Dengan menggunakan kode ini Anda sekarang dapat memecahkan kode apa pun Array<Any>
atau Dictionary<String, Any>
dengan sintaks yang sudah dikenal:
let dictionary: [String: Any] = try container.decode([String: Any].self, forKey: key)
atau
let array: [Any] = try container.decode([Any].self, forKey: key)
Sunting: ada satu peringatan yang saya temukan yaitu mendekodekan array kamus [[String: Any]]
. Sintaks yang diperlukan adalah sebagai berikut. Anda mungkin ingin membuat kesalahan alih-alih memaksa casting:
let items: [[String: Any]] = try container.decode(Array<Any>.self, forKey: .items) as! [[String: Any]]
EDIT 2: Jika Anda hanya ingin mengonversi seluruh file ke kamus, lebih baik Anda tetap menggunakan api dari JSONSerialization karena saya belum menemukan cara untuk memperluas JSONDecoder itu sendiri untuk langsung memecahkan kode kamus.
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
// appropriate error handling
return
}
Ekstensi
// Inspired by https://gist.github.com/mbuchetics/c9bc6c22033014aa0c550d3b4324411a
struct JSONCodingKeys: CodingKey {
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
var intValue: Int?
init?(intValue: Int) {
self.init(stringValue: "\(intValue)")
self.intValue = intValue
}
}
extension KeyedDecodingContainer {
func decode(_ type: Dictionary<String, Any>.Type, forKey key: K) throws -> Dictionary<String, Any> {
let container = try self.nestedContainer(keyedBy: JSONCodingKeys.self, forKey: key)
return try container.decode(type)
}
func decodeIfPresent(_ type: Dictionary<String, Any>.Type, forKey key: K) throws -> Dictionary<String, Any>? {
guard contains(key) else {
return nil
}
guard try decodeNil(forKey: key) == false else {
return nil
}
return try decode(type, forKey: key)
}
func decode(_ type: Array<Any>.Type, forKey key: K) throws -> Array<Any> {
var container = try self.nestedUnkeyedContainer(forKey: key)
return try container.decode(type)
}
func decodeIfPresent(_ type: Array<Any>.Type, forKey key: K) throws -> Array<Any>? {
guard contains(key) else {
return nil
}
guard try decodeNil(forKey: key) == false else {
return nil
}
return try decode(type, forKey: key)
}
func decode(_ type: Dictionary<String, Any>.Type) throws -> Dictionary<String, Any> {
var dictionary = Dictionary<String, Any>()
for key in allKeys {
if let boolValue = try? decode(Bool.self, forKey: key) {
dictionary[key.stringValue] = boolValue
} else if let stringValue = try? decode(String.self, forKey: key) {
dictionary[key.stringValue] = stringValue
} else if let intValue = try? decode(Int.self, forKey: key) {
dictionary[key.stringValue] = intValue
} else if let doubleValue = try? decode(Double.self, forKey: key) {
dictionary[key.stringValue] = doubleValue
} else if let nestedDictionary = try? decode(Dictionary<String, Any>.self, forKey: key) {
dictionary[key.stringValue] = nestedDictionary
} else if let nestedArray = try? decode(Array<Any>.self, forKey: key) {
dictionary[key.stringValue] = nestedArray
}
}
return dictionary
}
}
extension UnkeyedDecodingContainer {
mutating func decode(_ type: Array<Any>.Type) throws -> Array<Any> {
var array: [Any] = []
while isAtEnd == false {
// See if the current value in the JSON array is `null` first and prevent infite recursion with nested arrays.
if try decodeNil() {
continue
} else if let value = try? decode(Bool.self) {
array.append(value)
} else if let value = try? decode(Double.self) {
array.append(value)
} else if let value = try? decode(String.self) {
array.append(value)
} else if let nestedDictionary = try? decode(Dictionary<String, Any>.self) {
array.append(nestedDictionary)
} else if let nestedArray = try? decode(Array<Any>.self) {
array.append(nestedArray)
}
}
return array
}
mutating func decode(_ type: Dictionary<String, Any>.Type) throws -> Dictionary<String, Any> {
let nestedContainer = try self.nestedContainer(keyedBy: JSONCodingKeys.self)
return try nestedContainer.decode(type)
}
}
UnkeyedDecodingContainer
'sdecode(_ type: Array<Any>.Type) throws -> Array<Any>
adalah memeriksa untuk bersarang array yang. Jadi jika Anda memiliki struktur data yang terlihat seperti berikut:[true, 452.0, ["a", "b", "c"] ]
Ini akan menarik["a", "b", "c"]
array bersarang . Thedecode
Metode dariUnkeyedDecodingContainer
"muncul" off elemen dari wadah. Itu seharusnya tidak menyebabkan rekursi tak terbatas.{"array": null}
. Jadi Andaguard contains(key)
akan lulus tetapi akan crash beberapa baris kemudian ketika mencoba untuk memecahkan kode nilai null untuk kunci "array". Jadi lebih baik menambahkan satu kondisi lagi untuk memeriksa apakah nilainya sebenarnya bukan null sebelum memanggildecode
.} else if let nestedArray = try? decode(Array<Any>.self, forKey: key)
mencoba:} else if var nestedContainer = try? nestedUnkeyedContainer(), let nestedArray = try? nestedContainer.decode(Array<Any>.self) {
Saya telah bermain dengan masalah ini juga, dan akhirnya menulis perpustakaan sederhana untuk bekerja dengan tipe "JSON generik" . (Di mana "generik" berarti "tanpa struktur yang diketahui sebelumnya".) Poin utama mewakili JSON generik dengan tipe konkret:
Jenis ini kemudian dapat mengimplementasikan
Codable
danEquatable
.sumber
Anda dapat membuat struct metadata yang mengonfirmasi
Decodable
protokol dan menggunakanJSONDecoder
kelas untuk membuat objek dari data dengan menggunakan metode decode seperti di bawah inisumber
metadata
nilainya. Itu bisa berupa objek sewenang-wenang.metadata
dapat berupa objek JSON apa pun. Jadi bisa kamus kosong atau kamus apapun. "metadata": {} "metadata": {user_id: "id"} "metadata": {preferensi: {shows_value: true, language: "en"}} dll.Saya datang dengan solusi yang sedikit berbeda.
Misalkan kita memiliki sesuatu yang lebih dari yang sederhana
[String: Any]
untuk diurai, jika Any mungkin berupa larik atau kamus bersarang atau kamus larik.Sesuatu seperti ini:
Nah, inilah solusi saya:
Coba gunakan
sumber
Ketika saya menemukan jawaban lama, saya hanya menguji kasus objek JSON sederhana tetapi bukan yang kosong yang akan menyebabkan pengecualian runtime seperti @slurmomatic dan @zoul ditemukan. Maaf untuk masalah ini.
Jadi saya mencoba cara lain dengan memiliki protokol JSONValue sederhana, mengimplementasikan
AnyJSONValue
tipe erasure struct dan menggunakan tipe itu sebagai gantinyaAny
. Berikut implementasinya.Dan inilah cara menggunakannya saat mendekode
Masalah dengan masalah ini adalah kita harus menelepon
value.jsonValue as? Int
. Kita perlu menunggu sampaiConditional Conformance
mendarat di Swift, itu akan menyelesaikan masalah ini atau setidaknya membantunya menjadi lebih baik.[Jawaban Lama]
Saya memposting pertanyaan ini di forum Pengembang Apple dan ternyata itu sangat mudah.
dapat saya lakukan
di penginisialisasi.
Itu buruk bagiku untuk melewatkannya sejak awal.
sumber
Any
tidak sesuai denganDecodable
jadi saya tidak yakin bagaimana ini adalah jawaban yang benar.Jika Anda menggunakan SwiftyJSON untuk mengurai JSON, Anda dapat memperbarui ke 4.1.0 yang memiliki
Codable
dukungan protokol. Cukup nyatakanmetadata: JSON
dan Anda sudah siap.sumber
Anda mungkin pernah melihat BeyovaJSON
sumber
Cara termudah dan disarankan adalah dengan membuat model terpisah untuk setiap kamus atau model yang ada di JSON .
Inilah yang saya lakukan
Pemakaian:
** Saya telah menggunakan opsional untuk berada di sisi aman saat mengurai, dapat diubah sesuai kebutuhan.
Baca lebih lanjut tentang topik ini
sumber
Saya telah membuat pod untuk memudahkan cara decoding + encoding
[String: Any]
,[Any]
. Dan ini menyediakan encode atau decode properti opsional, di sini https://github.com/levantAJ/AnyCodableBagaimana cara menggunakannya:
sumber
Berikut ini lebih umum (tidak hanya
[String: Any]
, tetapi[Any]
pendekatan yang dapat diterjemahkan) dan dikemas (entitas terpisah digunakan untuk itu) terinspirasi oleh jawaban @loudmouth.Menggunakannya akan terlihat seperti:
JsonContainer
adalah entitas pembantu yang kami gunakan untuk membungkus decoding data JSON ke objek JSON (baik larik maupun kamus) tanpa memperluas*DecodingContainer
(sehingga tidak akan mengganggu kasus yang jarang terjadi saat objek JSON tidak dimaksudkan oleh[String: Any]
).Perhatikan bahwa tipe angka dan boolean didukung oleh
NSNumber
, jika tidak, hal seperti ini tidak akan berfungsi:sumber
decode menggunakan decoder dan kunci pengkodean
sumber
sumber