অভিধানে এনকোড করার জন্য আমি কীভাবে সুইফটের কোডড ব্যবহার করতে পারি?


110

আমার একটি স্ট্রাক্ট রয়েছে যা সুইফট 4 এর প্রয়োগ করে Codable। সেই স্ট্রাক্টটিকে অভিধানে এনকোড করার জন্য কি কোনও অন্তর্নির্মিত উপায় আছে?

let struct = Foo(a: 1, b: 2)
let dict = something(struct)
// now dict is ["a": 1, "b": 2]

উত্তর:


231

আপনার চারপাশে ডেটা স্থানান্তরিত করার কিছুটা আপত্তি না থাকলে আপনি এই জাতীয় কিছু ব্যবহার করতে পারেন:

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
  }
}

বা optionচ্ছিক বৈকল্পিক

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)), অভিধান / অ্যারে দিয়ে কোডেবলের সাথে সঙ্গতিপূর্ণ কোনও অবজেক্টটি এই উদ্যোগটি দেখুন


22

এখানে সহজ বাস্তবায়নের হয় 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/SwiftD शब्दकोश কোডিং 


অনেক অনেক ধন্যবাদ !, বিকল্পটি উত্তরাধিকার ব্যবহার করা হবে তবে কলিং সাইটটি অভিধান হিসাবে ধরণটি আবিষ্কার করতে সক্ষম হবে না কারণ সেখানে বিভিন্ন রিটার্নের ধরণের 2 ফাংশন থাকবে।
ব্যবহারকারী 1046037

17

আমি কোডেডফায়ারবেস নামে একটি লাইব্রেরি তৈরি করেছি এবং এর প্রাথমিক উদ্দেশ্য ছিল এটি ফায়ারবেস ডাটাবেস ব্যবহার করা, তবে এটি আসলে আপনার যা প্রয়োজন তা করে: এটি একটি ডিকশনারি বা অন্য কোনও ধরণের তৈরি করে ঠিক তেমনভাবেJSONDecoder তবে আপনাকে এখানে ডাবল রূপান্তর করার দরকার নেই don't যেমন আপনি অন্যান্য উত্তরে করেন। সুতরাং এটি দেখতে কিছু হবে:

import CodableFirebase

let model = Foo(a: 1, b: 2)
let dict = try! FirebaseEncoder().encode(model)

7

এটি সর্বোত্তম উপায় কিনা তা সম্পর্কে আমি নিশ্চিত নই তবে আপনি অবশ্যই এর মতো কিছু করতে পারেন:

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)

8
এটি কেবল একই ধরণের সমস্ত বৈশিষ্ট্যযুক্ত কাঠামোর জন্য কাজ করবে
লিও ডাবাস

1
আমি কেবল "লেট ডিক্ট = জেএসএসইএনডিকোডার () কে ডিকোড ([স্ট্রিং: ইন্ট]। নিজেই থেকে, জেএসওএনএনকোডার ()। এনকোড (ফু))" দিয়ে চেষ্টা করেছি এবং আমি পেয়েছি "ডিকোডরিং <স্ট্রিং, যে কোনও>> ডিকোডের প্রত্যাশিত পরিবর্তে অ্যারে। " আপনি
প্লিজ


6

এটি করার মতো কোনও বিল্ট নেই। উপরে উত্তর হিসাবে আপনার যদি কোনও পারফরম্যান্স সমস্যা না থাকে তবে আপনি 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)

উদাহরণটি আরও ছোট করার জন্য আমি এখানে জোর চেষ্টা করছি। প্রোডাকশন কোডে আপনার ত্রুটিগুলি যথাযথভাবে পরিচালনা করা উচিত।


4

কিছু প্রকল্পে, আমি দ্রুত প্রতিচ্ছবি ব্যবহার করেছি। তবে সতর্কতা অবলম্বন করুন, নেস্টেড কোডিং অবজেক্টগুলিও সেখানে ম্যাপ করা হয়নি।

let dict = Dictionary(uniqueKeysWithValues: Mirror(reflecting: foo).children.map{ ($0.label!, $0.value) })

2

আমি স্পষ্টভাবে মনে করি যে কেবল ব্যবহার করতে সক্ষম হওয়ার কিছু মূল্য রয়েছে Codable JSON / Plists / যাই হোক না কেন আঘাত করার অভিপ্রায় ছাড়াই কেবল অভিধানগুলিতে / থেকে এনকোড করতে । প্রচুর এপিআই রয়েছে যা আপনাকে কেবল একটি অভিধান ফিরিয়ে দেয়, বা অভিধানের প্রত্যাশা করে এবং এটিকে অনন্ত বয়লারপ্লেট কোড না লিখে সুইফট স্ট্রাক্টস বা অবজেক্টগুলির সাথে সহজেই সেগুলি বিনিময় করতে সক্ষম হওয়ায় খুব ভাল।

আমি ফাউন্ডেশন JSONEncoder.swift উত্সের উপর ভিত্তি করে কিছু কোড নিয়ে খেলছি (যা আসলে অভিধানের এনকোডিং / অভ্যন্তরীণ ডিকোডিং বাস্তবায়ন করে তবে তা রফতানি করে না)।

কোডটি এখানে পাওয়া যাবে: https://github.com/elegantchaos/D অভিধানCoding

এটি এখনও বেশ রুক্ষ, তবে আমি এটিকে কিছুটা প্রসারিত করেছি যাতে উদাহরণস্বরূপ, ডিকোডিংয়ের সময় এটি ডিফল্টের সাথে অনুপস্থিত মান পূরণ করতে পারে।


2

অভিধান থেকে বাইনারি ফর্ম্যাটে কেবল চূড়ান্ত সিরিয়ালাইজেশন সরিয়ে আমি সুইফট প্রকল্প থেকে প্রপার্টিলিস্ট এনকোডারকে একটি অভিধানকোষক হিসাবে পরিবর্তন করেছি। আপনি একই নিজে করতে পারেন, অথবা আপনি থেকে আমার কোড গ্রহণ করতে পারেন এখানে

এটি এর মতো ব্যবহার করা যেতে পারে:

do {
    let employeeDictionary: [String: Any] = try DictionaryEncoder().encode(employee)
} catch let error {
    // handle error
}

0

আমি একটি দ্রুত লিখেছিলেন সারকথা (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)
}

0

কোডডে এটি করার সরাসরি কোনও উপায় নেই। আপনার স্ট্রাক্টের জন্য আপনাকে এনকোডেবল / ডিকোডেবল প্রোটোকল প্রয়োগ করতে হবে। আপনার উদাহরণস্বরূপ, আপনাকে নীচের মতো লেখার প্রয়োজন হতে পারে

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)
    }
}

0

আমি এখানে একটি পড তৈরি করেছি 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)
    }
}

1
আপনার উদাহরণ কীভাবে সমস্যাটি সমাধান করবেন তা দেখায় না
সাইমন মোশেনকো

0

আপনি যদি SwiftyJSON ব্যবহার করে থাকেন তবে আপনি এরকম কিছু করতে পারেন:

JSON(data: JSONEncoder().encode(foo)).dictionaryObject

দ্রষ্টব্য: আপনিও এই অভিধানটি পাস করতে parametersপারেন Alamofire অনুরোধ।


0

এখানে একটি প্রোটোকল ভিত্তিক সমাধান রয়েছে:

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)\"")
}

0

এখানে অভিধান -> অবজেক্ট। সুইফট 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
        }
    }
}

-5

এটি চিন্তা করে দেখুন, সাধারণ ক্ষেত্রে প্রশ্নের উত্তর নেই, কারণ Encodableউদাহরণটি কোনও অভিধানে সিরিয়ালযোগ্য নয় এমন কিছু হতে পারে যেমন অ্যারে:

let payload = [1, 2, 3]
let encoded = try JSONEncoder().encode(payload) // "[1,2,3]"

তা ছাড়াও আমি ফ্রেমওয়ার্কের মতো কিছু লিখেছি ।


আমাকে স্বীকার করতে হবে আমি এখনও কেন বুঝতে পারছি না কেন এটি হ্রাস করা হয়েছে :–) ক্যাভেটটি সত্য নয়? বা কাঠামো কার্যকর না?
জোল
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.