जवाबों:
अगर आपको अपने आसपास के डेटा को शिफ्ट करने में जरा भी एतराज नहीं है, तो आप कुछ इस तरह इस्तेमाल कर सकते हैं:
extension Encodable {
func asDictionary() throws -> [String: Any] {
let data = try JSONEncoder().encode(self)
guard let dictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] else {
throw NSError()
}
return dictionary
}
}
या एक वैकल्पिक संस्करण
extension Encodable {
var dictionary: [String: Any]? {
guard let data = try? JSONEncoder().encode(self) else { return nil }
return (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)).flatMap { $0 as? [String: Any] }
}
}
मान लिया जाये कि Foo
के अनुरूप Codable
या वास्तव में Encodable
तो आप ऐसा कर सकते हैं।
let struct = Foo(a: 1, b: 2)
let dict = try struct.asDictionary()
let optionalDict = struct.dictionary
यदि आप दूसरे रास्ते पर जाना चाहते हैं ( init(any)
), इस Init पर एक नज़र डालें, जो एक शब्दकोष या कोड के साथ Codable के अनुरूप है।
यहाँ DictionaryEncoder
/ DictionaryDecoder
कि रैप के सरल कार्यान्वयन हैं JSONEncoder
, JSONDecoder
और JSONSerialization
, जो एन्कोडिंग / डिकोडिंग रणनीतियों को भी संभालते हैं ...
class DictionaryEncoder {
private let encoder = JSONEncoder()
var dateEncodingStrategy: JSONEncoder.DateEncodingStrategy {
set { encoder.dateEncodingStrategy = newValue }
get { return encoder.dateEncodingStrategy }
}
var dataEncodingStrategy: JSONEncoder.DataEncodingStrategy {
set { encoder.dataEncodingStrategy = newValue }
get { return encoder.dataEncodingStrategy }
}
var nonConformingFloatEncodingStrategy: JSONEncoder.NonConformingFloatEncodingStrategy {
set { encoder.nonConformingFloatEncodingStrategy = newValue }
get { return encoder.nonConformingFloatEncodingStrategy }
}
var keyEncodingStrategy: JSONEncoder.KeyEncodingStrategy {
set { encoder.keyEncodingStrategy = newValue }
get { return encoder.keyEncodingStrategy }
}
func encode<T>(_ value: T) throws -> [String: Any] where T : Encodable {
let data = try encoder.encode(value)
return try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String: Any]
}
}
class DictionaryDecoder {
private let decoder = JSONDecoder()
var dateDecodingStrategy: JSONDecoder.DateDecodingStrategy {
set { decoder.dateDecodingStrategy = newValue }
get { return decoder.dateDecodingStrategy }
}
var dataDecodingStrategy: JSONDecoder.DataDecodingStrategy {
set { decoder.dataDecodingStrategy = newValue }
get { return decoder.dataDecodingStrategy }
}
var nonConformingFloatDecodingStrategy: JSONDecoder.NonConformingFloatDecodingStrategy {
set { decoder.nonConformingFloatDecodingStrategy = newValue }
get { return decoder.nonConformingFloatDecodingStrategy }
}
var keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy {
set { decoder.keyDecodingStrategy = newValue }
get { return decoder.keyDecodingStrategy }
}
func decode<T>(_ type: T.Type, from dictionary: [String: Any]) throws -> T where T : Decodable {
let data = try JSONSerialization.data(withJSONObject: dictionary, options: [])
return try decoder.decode(type, from: data)
}
}
उपयोग JSONEncoder
/ के समान है JSONDecoder
...
let dictionary = try DictionaryEncoder().encode(object)
तथा
let object = try DictionaryDecoder().decode(Object.self, from: dictionary)
सुविधा के लिए, मैंने यह सब रेपो में डाल दिया है ... https://github.com/ashleymills/SwiftDictionaryCoding
मैंने CodableFirebase नाम से एक लाइब्रेरी बनाई है और इसका प्रारंभिक उद्देश्य फायरबेस डेटाबेस के साथ इसका उपयोग करना था, लेकिन यह वास्तव में आपको क्या चाहिए: यह एक शब्दकोश या किसी अन्य प्रकार की तरह ही बनाता है, JSONDecoder
लेकिन आपको यहां डबल रूपांतरण करने की आवश्यकता नहीं है जैसे आप अन्य उत्तरों में करते हैं। तो यह कुछ इस तरह दिखेगा:
import CodableFirebase
let model = Foo(a: 1, b: 2)
let dict = try! FirebaseEncoder().encode(model)
मुझे यकीन नहीं है कि यह सबसे अच्छा तरीका है लेकिन आप निश्चित रूप से ऐसा कुछ कर सकते हैं:
struct Foo: Codable {
var a: Int
var b: Int
init(a: Int, b: Int) {
self.a = a
self.b = b
}
}
let foo = Foo(a: 1, b: 2)
let dict = try JSONDecoder().decode([String: Int].self, from: JSONEncoder().encode(foo))
print(dict)
let dict = try JSONSerialization.jsonObject(with: try JSONEncoder().encode(struct), options: []) as? [String: Any]
ऐसा करने का कोई तरीका नहीं है। जैसा कि ऊपर बताया गया है कि यदि आपके पास कोई प्रदर्शन समस्या नहीं है, तो आप JSONEncoder
+ JSONSerialization
कार्यान्वयन को स्वीकार कर सकते हैं ।
लेकिन मैं मानक पुस्तकालय के लिए एक एनकोडर / डिकोडर ऑब्जेक्ट प्रदान करने के लिए जाना चाहूंगा।
class DictionaryEncoder {
private let jsonEncoder = JSONEncoder()
/// Encodes given Encodable value into an array or dictionary
func encode<T>(_ value: T) throws -> Any where T: Encodable {
let jsonData = try jsonEncoder.encode(value)
return try JSONSerialization.jsonObject(with: jsonData, options: .allowFragments)
}
}
class DictionaryDecoder {
private let jsonDecoder = JSONDecoder()
/// Decodes given Decodable type from given array or dictionary
func decode<T>(_ type: T.Type, from json: Any) throws -> T where T: Decodable {
let jsonData = try JSONSerialization.data(withJSONObject: json, options: [])
return try jsonDecoder.decode(type, from: jsonData)
}
}
आप इसे निम्नलिखित कोड के साथ आज़मा सकते हैं:
struct Computer: Codable {
var owner: String?
var cpuCores: Int
var ram: Double
}
let computer = Computer(owner: "5keeve", cpuCores: 8, ram: 4)
let dictionary = try! DictionaryEncoder().encode(computer)
let decodedComputer = try! DictionaryDecoder().decode(Computer.self, from: dictionary)
उदाहरण को छोटा करने के लिए मैं यहाँ बल-प्रयास कर रहा हूँ। उत्पादन कोड में आपको त्रुटियों को उचित रूप से संभालना चाहिए।
कुछ प्रोजेक्ट में, मैंने स्विफ्ट प्रतिबिंब का उपयोग किया है। लेकिन सावधान रहें, नीडिबल कोडेबल ऑब्जेक्ट्स, वहां भी मैप नहीं किए जाते हैं।
let dict = Dictionary(uniqueKeysWithValues: Mirror(reflecting: foo).children.map{ ($0.label!, $0.value) })
मुझे निश्चित रूप से लगता है कि Codable
JSON / Plists / जो भी हो, को मारने के इरादे के बिना, शब्दकोशों से / सांकेतिक शब्दों में बदलना / उपयोग करने में सक्षम होने में कुछ मूल्य है । बहुत सारे एपीआई हैं जो आपको केवल एक शब्दकोश वापस देते हैं, या एक शब्दकोश की अपेक्षा करते हैं, और बिना किसी स्विफ्ट बॉयलरप्लेट कोड के लिखने के लिए स्विफ्ट के ढांचे या वस्तुओं के साथ उन्हें आसानी से इंटरचेंज करने में सक्षम होना अच्छा है।
मैं फाउंडेशन JSONEncoder.swift स्रोत के आधार पर कुछ कोड के साथ गोल खेल रहा हूं (जो वास्तव में शब्दकोश एन्कोडिंग / डिकोडिंग को आंतरिक रूप से लागू करता है, लेकिन इसे निर्यात नहीं करता है)।
कोड यहां पाया जा सकता है: https://github.com/elegantchaos/DictionaryCoding
यह अभी भी काफी कठिन है, लेकिन मैंने इसे थोड़ा विस्तारित किया है, उदाहरण के लिए, यह डिकोडिंग के दौरान चूक के साथ लापता मूल्यों को भर सकता है।
मैंने Swift प्रोजेक्ट से PropertyListEncoder को एक DictionaryEncoder में बदल दिया है, बस द्विआधारी प्रारूप में शब्दकोश से अंतिम क्रमांकन हटाकर। आप स्वयं भी ऐसा कर सकते हैं, या आप यहां से मेरा कोड ले सकते हैं
इसका उपयोग इस तरह किया जा सकता है:
do {
let employeeDictionary: [String: Any] = try DictionaryEncoder().encode(employee)
} catch let error {
// handle error
}
मैं एक त्वरित लिखा सार (Codable प्रोटोकॉल का उपयोग नहीं) इस संभालने के लिए। सावधान रहें, यह किसी भी मान को टाइप-चेक नहीं करता है और जो एनकोडेबल हैं उन मूल्यों पर पुनरावृत्ति नहीं करता है।
class DictionaryEncoder {
var result: [String: Any]
init() {
result = [:]
}
func encode(_ encodable: DictionaryEncodable) -> [String: Any] {
encodable.encode(self)
return result
}
func encode<T, K>(_ value: T, key: K) where K: RawRepresentable, K.RawValue == String {
result[key.rawValue] = value
}
}
protocol DictionaryEncodable {
func encode(_ encoder: DictionaryEncoder)
}
कोडेबल में ऐसा करने का कोई सीधा आगे का तरीका नहीं है। आपको अपनी संरचना के लिए एनकोडेबल / डिकोडेबल प्रोटोकॉल को लागू करना होगा। अपने उदाहरण के लिए, आपको नीचे लिखने की आवश्यकता हो सकती है
typealias EventDict = [String:Int]
struct Favorite {
var all:EventDict
init(all: EventDict = [:]) {
self.all = all
}
}
extension Favorite: Encodable {
struct FavoriteKey: CodingKey {
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
var intValue: Int? { return nil }
init?(intValue: Int) { return nil }
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: FavoriteKey.self)
for eventId in all {
let nameKey = FavoriteKey(stringValue: eventId.key)!
try container.encode(eventId.value, forKey: nameKey)
}
}
}
extension Favorite: Decodable {
public init(from decoder: Decoder) throws {
var events = EventDict()
let container = try decoder.container(keyedBy: FavoriteKey.self)
for key in container.allKeys {
let fav = try container.decode(Int.self, forKey: key)
events[key.stringValue] = fav
}
self.init(all: events)
}
}
मैंने यहां एक पॉड बनाया है https://github.com/levantAJ/AnyCodable को डीकोड और एनकोड की सुविधा देना [String: Any]
और[Any]
pod 'DynamicCodable', '1.0'
और आप डिकोड और एनकोड करने में सक्षम हैं [String: Any]
और[Any]
import DynamicCodable
struct YourObject: Codable {
var dict: [String: Any]
var array: [Any]
var optionalDict: [String: Any]?
var optionalArray: [Any]?
enum CodingKeys: String, CodingKey {
case dict
case array
case optionalDict
case optionalArray
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
dict = try values.decode([String: Any].self, forKey: .dict)
array = try values.decode([Any].self, forKey: .array)
optionalDict = try values.decodeIfPresent([String: Any].self, forKey: .optionalDict)
optionalArray = try values.decodeIfPresent([Any].self, forKey: .optionalArray)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(dict, forKey: .dict)
try container.encode(array, forKey: .array)
try container.encodeIfPresent(optionalDict, forKey: .optionalDict)
try container.encodeIfPresent(optionalArray, forKey: .optionalArray)
}
}
यदि आप SwiftyJSON का उपयोग कर रहे हैं , तो आप ऐसा कुछ कर सकते हैं:
JSON(data: JSONEncoder().encode(foo)).dictionaryObject
नोट: आप इस शब्दकोश
parameters
को आलमोफायर अनुरोधों के रूप में भी पारित कर सकते हैं ।
यहाँ एक प्रोटोकॉल आधारित समाधान है:
protocol DictionaryEncodable {
func encode() throws -> Any
}
extension DictionaryEncodable where Self: Encodable {
func encode() throws -> Any {
let jsonData = try JSONEncoder().encode(self)
return try JSONSerialization.jsonObject(with: jsonData, options: .allowFragments)
}
}
protocol DictionaryDecodable {
static func decode(_ dictionary: Any) throws -> Self
}
extension DictionaryDecodable where Self: Decodable {
static func decode(_ dictionary: Any) throws -> Self {
let jsonData = try JSONSerialization.data(withJSONObject: dictionary, options: [])
return try JSONDecoder().decode(Self.self, from: jsonData)
}
}
typealias DictionaryCodable = DictionaryEncodable & DictionaryDecodable
और यहाँ इसका उपयोग कैसे करना है:
class AClass: Codable, DictionaryCodable {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
struct AStruct: Codable, DictionaryEncodable, DictionaryDecodable {
var name: String
var age: Int
}
let aClass = AClass(name: "Max", age: 24)
if let dict = try? aClass.encode(), let theClass = try? AClass.decode(dict) {
print("Encoded dictionary: \n\(dict)\n\ndata from decoded dictionary: \"name: \(theClass.name), age: \(theClass.age)\"")
}
let aStruct = AStruct(name: "George", age: 30)
if let dict = try? aStruct.encode(), let theStruct = try? AStruct.decode(dict) {
print("Encoded dictionary: \n\(dict)\n\ndata from decoded dictionary: \"name: \(theStruct.name), age: \(theStruct.age)\"")
}
यहाँ शब्दकोश है -> वस्तु। स्विफ्ट 5।
extension Dictionary where Key == String, Value: Any {
func object<T: Decodable>() -> T? {
if let data = try? JSONSerialization.data(withJSONObject: self, options: []) {
return try? JSONDecoder().decode(T.self, from: data)
} else {
return nil
}
}
}
यह सोचने के लिए आओ, सवाल का सामान्य मामले में जवाब नहीं है, क्योंकि Encodable
उदाहरण एक शब्दकोष में क्रमिक नहीं हो सकता है, जैसे कि एक सरणी:
let payload = [1, 2, 3]
let encoded = try JSONEncoder().encode(payload) // "[1,2,3]"
इसके अलावा, मैंने एक रूपरेखा के समान कुछ लिखा है ।