स्विफ्ट 4 के कोडेबल से गुणों को बाहर कैसे करें


104

स्विफ्ट 4 के नए Encodable/ Decodableप्रोटोकॉल JSON (डी) के क्रमांकन को काफी सुखद बनाते हैं। हालाँकि, मुझे अभी तक ठीक-ठाक नियंत्रण का कोई तरीका नहीं मिला है कि किन संपत्तियों को एनकोड किया जाए और जिन्हें डिकोड किया जाए।

मैंने देखा है कि संपत्ति को साथ रखने वाली संपत्ति को छोड़कर CodingKeysपूरी तरह से संपत्ति को प्रक्रिया से बाहर रखा गया है, लेकिन क्या अधिक ठीक-ठाक नियंत्रण रखने का कोई तरीका है?


क्या आप कह रहे हैं कि आपके पास एक मामला है, जिसमें आपके पास कुछ गुण हैं, जिन्हें आप एनकोड करना चाहते हैं, लेकिन अलग-अलग प्रॉपर्टीज जिन्हें आप डीकोड करना चाहते हैं? (अर्थात आप चाहते हैं कि आपका प्रकार गोल-तिगुना न हो?) क्योंकि यदि आप संपत्ति को छोड़कर, इसे एक डिफ़ॉल्ट मान देते हैं और इसे एनम से बाहर छोड़ना CodingKeysपर्याप्त है।
इटाई फेरर

भले ही, आप हमेशा प्रक्रिया पर पूर्ण नियंत्रण के लिए Codableप्रोटोकॉल ( init(from:)और encode(to:)) की आवश्यकताओं को मैन्युअल रूप से लागू कर सकते हैं ।
इताई फेर

मेरा विशिष्ट उपयोग मामला डिकोडर को बहुत अधिक नियंत्रण देने से बचने के लिए है, जो आंतरिक संपत्ति मूल्यों को अधिलेखित करने से JSON को दूरस्थ रूप से प्राप्त कर सकता है। नीचे दिए गए समाधान पर्याप्त हैं!
रामविलामट

1
मैं एक उत्तर / नई स्विफ्ट सुविधा देखना चाहता हूं, जिसमें केवल उन सभी गुणों को फिर से लागू करने के बजाय विशेष मामलों और बहिष्कृत कुंजी को संभालने की आवश्यकता होती है, जिन्हें आपको सामान्य रूप से मुफ्त में प्राप्त करना चाहिए।
pkamb

जवाबों:


182

एन्कोड / डिकोड करने के लिए कुंजियों की सूची को एक प्रकार से नियंत्रित किया जाता है जिसे कहा जाता है CodingKeys( sअंत में ध्यान दें )। संकलक आपके लिए इसे संश्लेषित कर सकता है लेकिन हमेशा इसे ओवरराइड कर सकता है।

मान लें कि आप संपत्तियों nicknameको एन्कोडिंग और डिकोडिंग दोनों से बाहर करना चाहते हैं :

struct Person: Codable {
    var firstName: String
    var lastName: String
    var nickname: String?

    private enum CodingKeys: String, CodingKey {
        case firstName, lastName
    }
}

यदि आप इसे असममित होना चाहते हैं (अर्थात एनकोड करें लेकिन डीकोड या इसके विपरीत नहीं), तो आपको अपना स्वयं का कार्यान्वयन प्रदान करना होगा encode(with encoder: )और init(from decoder: ):

struct Person: Codable {
    var firstName: String
    var lastName: String

    // Since fullName is a computed property, it's excluded by default
    var fullName: String {
        return firstName + " " + lastName
    }

    private enum CodingKeys: String, CodingKey {
        case firstName
        case lastName
        case fullName
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        firstName = try container.decode(String.self, forKey: .firstName)
        lastName = try container.decode(String.self, forKey: .lastName)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(firstName, forKey: .firstName)
        try container.encode(lastName, forKey: .lastName)
        try container.encode(fullName, forKey: .fullName)
    }
}

17
काम करने के लिए आपको nicknameएक डिफ़ॉल्ट मान देने की आवश्यकता है । अन्यथा, ऐसा कोई मूल्य नहीं है जिसे संपत्ति को सौंपा जा सके init(from:)
इटाई फेरर

1
@ ItaiFerber मैंने इसे एक वैकल्पिक में बदल दिया, जो मूल रूप से मेरे Xcode में था
कोड अलग

क्या आप वाकई encodeअसममित उदाहरण में प्रदान करना चाहते हैं? चूंकि यह अभी भी मानक व्यवहार है, मुझे नहीं लगता कि इसकी आवश्यकता थी। बस जब decodeसे विषमता आ रही है।
मार्क ए। डोनोहे

1
@MarqueIV हाँ, आपको करना होगा। चूंकि fullNameकिसी संग्रहीत संपत्ति में मैप नहीं किया जा सकता है, इसलिए आपको एक कस्टम एनकोडर और डिकोडर प्रदान करना होगा।
कोड अलग

2

यदि हमें संरचना में गुणों के एक बड़े सेट से कुछ गुणों के डिकोडिंग को बाहर करने की आवश्यकता है, तो उन्हें वैकल्पिक गुणों के रूप में घोषित करें। कोडिंग को अल्टरनेट करने के लिए कोड कोडिंगकेई एनम के तहत बहुत सारी चाबियां लिखने से कम है।

मैं संकलित आवृत्ति गुण और संगणित प्रकार गुण जोड़ने के लिए एक्सटेंशन का उपयोग करने की सलाह दूंगा। यह अन्य तर्क से कोड करने योग्य गुण को अलग करता है इसलिए बेहतर पठनीयता प्रदान करता है।


2

एनकोडर से कुछ गुणों को बाहर करने का एक और तरीका, अलग कोडिंग कंटेनर का उपयोग किया जा सकता है

struct Person: Codable {
    let firstName: String
    let lastName: String
    let excludedFromEncoder: String

    private enum CodingKeys: String, CodingKey {
        case firstName
        case lastName
    }
    private enum AdditionalCodingKeys: String, CodingKey {
        case excludedFromEncoder
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let anotherContainer = try decoder.container(keyedBy: AdditionalCodingKeys.self)
        firstName = try container.decode(String.self, forKey: .firstName)
        lastName = try container.decode(String.self, forKey: .lastName)

        excludedFromEncoder = try anotherContainer(String.self, forKey: . excludedFromEncoder)
    }

    // it is not necessary to implement custom encoding
    // func encode(to encoder: Encoder) throws

    // let person = Person(firstName: "fname", lastName: "lname", excludedFromEncoder: "only for decoding")
    // let jsonData = try JSONEncoder().encode(person)
    // let jsonString = String(data: jsonData, encoding: .utf8)
    // jsonString --> {"firstName": "fname", "lastName": "lname"}

}

डिकोडर के लिए एक ही दृष्टिकोण का उपयोग किया जा सकता है


1

आप गणना किए गए गुणों का उपयोग कर सकते हैं:

struct Person: Codable {
  var firstName: String
  var lastName: String
  var nickname: String?

  var nick: String {
    get {
      nickname ?? ""
    }
  }

  private enum CodingKeys: String, CodingKey {
    case firstName, lastName
  }
}

यह मेरे लिए सुराग था - एक lazy varप्रभावी ढंग से इसे रनटाइम प्रॉपर्टी बनाकर कोडेबल से बाहर करने का।
क्रिस

0

जबकि यह किया जा सकता है अंत में यह बहुत ही अनिश्चितता और यहां तक ​​कि असंगत होने के कारण समाप्त होता है । मुझे लगता है कि मैं देख रहा हूं कि आप कहां से आ रहे हैं, एस की अवधारणा #idHTML में प्रचलित है, लेकिन यह शायद ही कभी दुनिया में ले जाया जाता है, JSONजिसे मैं एक अच्छी बात मानता हूं (टीएम) ।

कुछ Codableसंरचनाएं आपकी JSONफ़ाइल को ठीक से पार्स करने में सक्षम होंगी यदि आप इसे पुनरावर्ती हैश का उपयोग करके पुनर्गठन करते हैं, अर्थात यदि आपके recipeबस में एक सरणी होती है ingredientsजिसमें बदले में (एक या कई) होते हैं ingredient_info। इस तरह से पार्सर आपको अपने नेटवर्क को पहली बार एक साथ सिलाई करने में मदद करेगा और आपको केवल एक सरल ट्रैवर्सल संरचना के माध्यम से कुछ बैकलिंक्स प्रदान करने होंगे यदि आपको वास्तव में उनकी आवश्यकता है । चूँकि इसके लिए आपकी JSONऔर आपकी डेटा संरचना की गहन आवश्यकता होती है, इसलिए मैं केवल इस बारे में सोचने के लिए आपके विचार को स्केच करता हूँ। यदि आप इसे स्वीकार करते हैं, तो कृपया मुझे टिप्पणियों में बताएं, फिर मैं इसे और विस्तृत कर सकता हूं, लेकिन उन परिस्थितियों के आधार पर आप उनमें से किसी एक को बदलने के लिए स्वतंत्रता में नहीं हो सकते।


0

मैंने सेट और इमेज (या किसी भी संपत्ति जिसे कोडेबल से बाहर रखा जाना चाहिए) संपत्ति प्राप्त करने के लिए एसोसिएटेडऑब्जेक्ट के साथ प्रोटोकॉल और इसके विस्तार का उपयोग किया है।

इसके साथ हमें अपने एनकोडर और डिकोडर को भी लागू नहीं करना है

यहाँ कोड है, सादगी के लिए प्रासंगिक कोड रखते हुए:

protocol SCAttachmentModelProtocol{
    var image:UIImage? {get set}
    var anotherProperty:Int {get set}
}
extension SCAttachmentModelProtocol where Self: SCAttachmentUploadRequestModel{
    var image:UIImage? {
        set{
            //Use associated object property to set it
        }
        get{
            //Use associated object property to get it
        }
    }
}
class SCAttachmentUploadRequestModel : SCAttachmentModelProtocol, Codable{
    var anotherProperty:Int
}

अब, जब भी हम इमेज प्रॉपर्टी को एक्सेस करना चाहते हैं, हम प्रोटोकॉल (SCAttachmentModelrotrot) की पुष्टि करने वाले ऑब्जेक्ट पर उपयोग कर सकते हैं

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.