स्विफ्ट 4 डिकोडेबल प्रोटोकॉल में JSON डिक्शनरी के प्रकार के साथ एक संपत्ति को कैसे डीकोड किया जाए


104

मान लें कि मेरे पास Customerडेटा प्रकार है जिसमें ऐसी metadataसंपत्ति है जिसमें ग्राहक ऑब्जेक्ट में कोई भी JSON शब्दकोश हो सकता है

struct Customer {
  let id: String
  let email: String
  let metadata: [String: Any]
}

{  
  "object": "customer",
  "id": "4yq6txdpfadhbaqnwp3",
  "email": "john.doe@example.com",
  "metadata": {
    "link_id": "linked-id",
    "buy_count": 4
  }
}

metadataसंपत्ति किसी भी मनमाने ढंग से JSON नक्शा वस्तु हो सकता है।

इससे पहले कि मैं एक deserialized JSON से संपत्ति कास्ट कर सकता हूं NSJSONDeserialization लेकिन नए स्विफ्ट 4 Decodableप्रोटोकॉल के साथ, मैं अभी भी ऐसा करने का तरीका नहीं सोच सकता।

क्या किसी को पता है कि स्विफ्ट 4 में इसे कैसे प्राप्त किया जा सकता है?

जवाबों:


90

इस जिस्ट से मिली प्रेरणा के साथ , मैंने कुछ एक्सटेंशन के लिए UnkeyedDecodingContainerऔर लिखा है KeyedDecodingContainer। आप यहाँ मेरे लिंग का लिंक पा सकते हैं । इस कोड का उपयोग करके आप अब किसी भी Array<Any>या संबंधित Dictionary<String, Any>वाक्यविन्यास के साथ डीकोड कर सकते हैं :

let dictionary: [String: Any] = try container.decode([String: Any].self, forKey: key)

या

let array: [Any] = try container.decode([Any].self, forKey: key)

संपादित करें: एक कैविएट है जिसे मैंने पाया है जो शब्दकोशों [[String: Any]]की एक सरणी को डिकोड कर रहा है । आवश्यक सिंटैक्स निम्नानुसार है। आप बल कास्टिंग के बजाय एक त्रुटि फेंकना चाहते हैं:

let items: [[String: Any]] = try container.decode(Array<Any>.self, forKey: .items) as! [[String: Any]]

EDIT 2: यदि आप पूरी फाइल को एक डिक्शनरी में बदलना चाहते हैं, तो आप JSONSerialization से एपी से चिपके रहना बेहतर है क्योंकि मैंने JSONDecoder को सीधे डिक्शनरी में बदलने का एक तरीका नहीं निकाला है।

guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
  // appropriate error handling
  return
}

एक्सटेंशन

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

दिलचस्प है, मैं इस बात की कोशिश करूँगा और परिणाम को आप को अपडेट करूंगा @loudmouth
Pitiphong Phongpattranont

@PitiphongPhongpattranont इस कोड ने आपके लिए काम किया?
जोर

1
में में पिछले हालत @JonBrooks UnkeyedDecodingContainer's decode(_ type: Array<Any>.Type) throws -> Array<Any>एक के लिए जाँच कर रहा है नेस्टेड सरणी। इसलिए यदि आपके पास एक डेटा संरचना है जो निम्न की तरह दिखता है: [true, 452.0, ["a", "b", "c"] ] यह नेस्टेड ["a", "b", "c"]सरणी को खींचेगा । कंटेनर से तत्व को "पॉप" करने की decodeविधि UnkeyedDecodingContainer। यह अनंत पुनरावृत्ति का कारण नहीं होना चाहिए।
जोर से

1
@ लॉउडमाउथ यह संभव है कि json में कुंजियों के लिए शून्य मान हो {"array": null}:। तो आपका guard contains(key)पास हो जाएगा लेकिन कुंजी "सरणी" के लिए शून्य मान को डिकोड करने का प्रयास करने पर यह बाद में कुछ पंक्तियों को क्रैश कर देगा। इसलिए यह जांचने के लिए एक और शर्त जोड़ना बेहतर है कि क्या कॉल करने से पहले मूल्य वास्तव में अशक्त नहीं है decode
चेरब

2
मुझे एक ठीक लगा: } else if let nestedArray = try? decode(Array<Any>.self, forKey: key)कोशिश करने के बजाय :} else if var nestedContainer = try? nestedUnkeyedContainer(), let nestedArray = try? nestedContainer.decode(Array<Any>.self) {
जॉन ब्रूक्स

23

मैंने इस समस्या के साथ भी खेला है, और अंत में "सामान्य JSON" प्रकार के साथ काम करने के लिए एक सरल पुस्तकालय लिखा है । (जहां "सामान्य" का अर्थ है "अग्रिम में ज्ञात संरचना के साथ नहीं।") मुख्य बिंदु सामान्य JSON का प्रतिनिधित्व एक ठोस प्रकार के साथ कर रहा है:

public enum JSON {
    case string(String)
    case number(Float)
    case object([String:JSON])
    case array([JSON])
    case bool(Bool)
    case null
}

यह प्रकार तब लागू हो सकता है Codableऔर Equatable


13

आप मेटाडेटा संरचना बना सकते हैं जो Decodableप्रोटोकॉल की पुष्टि करता है और JSONDecoderनीचे की तरह डिकोड विधि का उपयोग करके डेटा से ऑब्जेक्ट बनाने के लिए क्लास का उपयोग करता है

let json: [String: Any] = [
    "object": "customer",
    "id": "4yq6txdpfadhbaqnwp3",
    "email": "john.doe@example.com",
    "metadata": [
        "link_id": "linked-id",
        "buy_count": 4
    ]
]

struct Customer: Decodable {
    let object: String
    let id: String
    let email: String
    let metadata: Metadata
}

struct Metadata: Decodable {
    let link_id: String
    let buy_count: Int
}

let data = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)

let decoder = JSONDecoder()
do {
    let customer = try decoder.decode(Customer.self, from: data)
    print(customer)
} catch {
    print(error.localizedDescription)
}

10
नहीं, मैं नहीं कर सकता, क्योंकि मुझे metadataमूल्य की संरचना का पता नहीं है । यह कोई भी मनमानी वस्तु हो सकती है।
पिटिपहॉन्ग फोंगपट्ट्रान्ट

क्या आपका मतलब है कि यह ऐरे या डिक्शनरी प्रकार हो सकता है?
सुहित पाटिल

क्या आप उदाहरण दे सकते हैं या मेटाडेटा संरचना के बारे में अधिक
व्याख्या

2
metadataकिसी भी JSON ऑब्जेक्ट का मान हो सकता है। तो यह खाली शब्दकोष या कोई शब्दकोश हो सकता है। "मेटाडेटा": {} "मेटाडेटा": {user_id: "id"} "मेटाडेटा": {वरीयता: {शोज़_वल्यू: ट्रू, लैंग्वेज: "एन"}} इत्यादि
पिटिपहॉंग फोन्गाप्टोनॉट

एक संभव विकल्प मेटाडेटा संरचना में सभी पैरामेट्स को वैकल्पिक के रूप में उपयोग करना और मेटाडेटा संरचना में सभी संभावित मूल्यों को सूचीबद्ध करना होगा जैसे कि संरचना मेटाडेटा {var user_id: स्ट्रिंग? वर वरीयता: स्ट्रिंग? }
सुहित पाटिल

8

मैं थोड़ा अलग समाधान के साथ आया था।

मान लें कि हमारे पास [String: Any]पार्स करने के लिए एक सरल से अधिक कुछ है किसी भी एक सरणी या नेस्टेड शब्दकोश या सरणियों का शब्दकोश हो सकता है।

कुछ इस तरह:

var json = """
{
  "id": 12345,
  "name": "Giuseppe",
  "last_name": "Lanza",
  "age": 31,
  "happy": true,
  "rate": 1.5,
  "classes": ["maths", "phisics"],
  "dogs": [
    {
      "name": "Gala",
      "age": 1
    }, {
      "name": "Aria",
      "age": 3
    }
  ]
}
"""

खैर, यह मेरा समाधान है:

public struct AnyDecodable: Decodable {
  public var value: Any

  private struct CodingKeys: CodingKey {
    var stringValue: String
    var intValue: Int?
    init?(intValue: Int) {
      self.stringValue = "\(intValue)"
      self.intValue = intValue
    }
    init?(stringValue: String) { self.stringValue = stringValue }
  }

  public init(from decoder: Decoder) throws {
    if let container = try? decoder.container(keyedBy: CodingKeys.self) {
      var result = [String: Any]()
      try container.allKeys.forEach { (key) throws in
        result[key.stringValue] = try container.decode(AnyDecodable.self, forKey: key).value
      }
      value = result
    } else if var container = try? decoder.unkeyedContainer() {
      var result = [Any]()
      while !container.isAtEnd {
        result.append(try container.decode(AnyDecodable.self).value)
      }
      value = result
    } else if let container = try? decoder.singleValueContainer() {
      if let intVal = try? container.decode(Int.self) {
        value = intVal
      } else if let doubleVal = try? container.decode(Double.self) {
        value = doubleVal
      } else if let boolVal = try? container.decode(Bool.self) {
        value = boolVal
      } else if let stringVal = try? container.decode(String.self) {
        value = stringVal
      } else {
        throw DecodingError.dataCorruptedError(in: container, debugDescription: "the container contains nothing serialisable")
      }
    } else {
      throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not serialise"))
    }
  }
}

इसका उपयोग करके देखें

let stud = try! JSONDecoder().decode(AnyDecodable.self, from: jsonData).value as! [String: Any]
print(stud)

6

जब मुझे पुराना उत्तर मिला, तो मैंने केवल एक साधारण JSON ऑब्जेक्ट केस का परीक्षण किया, लेकिन खाली नहीं जो @slurmomatic और @zoul जैसे रनटाइम अपवाद का कारण बने। इस मुद्दे के लिए क्षमा करें।

इसलिए मैं एक सरल JSONValue प्रोटोकॉल के द्वारा दूसरा तरीका आज़माता हूं, इरेज़र AnyJSONValueसंरचना को लागू करता हूं और इसके बजाय उस प्रकार का उपयोग करता हूं Any। यहाँ एक कार्यान्वयन है।

public protocol JSONType: Decodable {
    var jsonValue: Any { get }
}

extension Int: JSONType {
    public var jsonValue: Any { return self }
}
extension String: JSONType {
    public var jsonValue: Any { return self }
}
extension Double: JSONType {
    public var jsonValue: Any { return self }
}
extension Bool: JSONType {
    public var jsonValue: Any { return self }
}

public struct AnyJSONType: JSONType {
    public let jsonValue: Any

    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()

        if let intValue = try? container.decode(Int.self) {
            jsonValue = intValue
        } else if let stringValue = try? container.decode(String.self) {
            jsonValue = stringValue
        } else if let boolValue = try? container.decode(Bool.self) {
            jsonValue = boolValue
        } else if let doubleValue = try? container.decode(Double.self) {
            jsonValue = doubleValue
        } else if let doubleValue = try? container.decode(Array<AnyJSONType>.self) {
            jsonValue = doubleValue
        } else if let doubleValue = try? container.decode(Dictionary<String, AnyJSONType>.self) {
            jsonValue = doubleValue
        } else {
            throw DecodingError.typeMismatch(JSONType.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Unsupported JSON tyep"))
        }
    }
}

और यहां डिकोडिंग के दौरान इसका उपयोग कैसे किया जाए

metadata = try container.decode ([String: AnyJSONValue].self, forKey: .metadata)

इस समस्या के साथ समस्या यह है कि हमें कॉल करना होगा value.jsonValue as? Int। हमें Conditional Conformanceस्विफ्ट में जमीन तक इंतजार करने की आवश्यकता है , जो इस समस्या को हल करेगा या कम से कम इसे बेहतर बनाने में मदद करेगा।


[पुराना उत्तर]

मैं इस प्रश्न को Apple डेवलपर फोरम पर पोस्ट करता हूं और यह पता चलता है कि यह बहुत आसान है।

मैं कर सकता हूँ

metadata = try container.decode ([String: Any].self, forKey: .metadata)

इनिशियलाइज़र में।

पहली बार में यह याद करना मेरा बुरा था।


4
Apple डेवलपर पर सवाल करने के लिए लिंक पोस्ट कर सकता है। Anyके अनुरूप नहीं है Decodableतो मैं कर रहा हूँ इस यकीन है कि कैसे नहीं सही जवाब है।
रेजा शिराज़ियन

@RezaShirazian यही मैंने पहले स्थान पर सोचा था। लेकिन यह पता चलता है कि डिक्शनरी एनकोडेबल के अनुरूप है जब इसकी चाबियां हसबेट के अनुरूप हैं और इसके मूल्यों पर निर्भर नहीं हैं। आप डिक्शनरी हेडर को खोल सकते हैं और इसे खुद देख सकते हैं। विस्तार शब्दकोश: एन्कोडेबल जहाँ कुंजी: हस्सैबल एक्सटेंशन डिक्शनरी: डिकोडेबल जहाँ कुंजी: हैज़ेबल
फ़ॉर्म्स .developer.apple.com/

6
वर्तमान में यह काम नहीं करता है। "डिक्शनरी <स्ट्रींग, एनी> डिसोडेबल के अनुरूप नहीं है क्योंकि कोई भी डिकोडेबल के अनुरूप नहीं है"
mbuchetics

यह काम करता है। मैं इसे अपने कोड में उपयोग कर रहा हूं। आपको यह समझने की आवश्यकता है कि इस आवश्यकता को व्यक्त करने का कोई तरीका नहीं है कि "डिक्शनरी ऑफ डिक्शनरी को डिकोडेबल प्रोटोकॉल के अनुरूप होना चाहिए, ताकि डिक्शनरी को डेकोडेबल प्रोटोकॉल के अनुरूप बनाया जा सके"। यह "सशर्त अनुरूपता" है जो स्विफ्ट 4 में अभी तक लागू नहीं हुई है, मुझे लगता है कि यह अभी के लिए ठीक है क्योंकि स्विफ्ट टाइप सिस्टम (और जेनरिक) में बहुत सी सीमाएं हैं। तो यह अभी के लिए काम करता है, लेकिन जब भविष्य में स्विफ्ट टाइप सिस्टम में सुधार होता है (विशेषकर जब कंडिशनल कंफॉर्मेंस लागू होता है), तो यह काम नहीं करना चाहिए।
पिटिफ़ॉन्ग फ़ॉन्गपट्रानॉन्ट

3
मेरे लिए Xcode 9 बीटा 5 के रूप में काम नहीं करता है। संकलन करता है, लेकिन रनटाइम में उड़ा देता है: शब्दकोश <स्ट्रिंग, कोई भी> Decodable के अनुरूप नहीं है क्योंकि कोई भी Decodable के अनुरूप नहीं है।
zoul

6

यदि आप JSON को पार्स करने के लिए SwiftyJSON का उपयोग करते हैं , तो आप 4.1.0 पर अपडेट कर सकते हैं जिसमें Codableप्रोटोकॉल समर्थन है। बस घोषित करें metadata: JSONऔर आप सभी सेट हैं।

import SwiftyJSON

struct Customer {
  let id: String
  let email: String
  let metadata: JSON
}

मुझे नहीं पता कि यह जवाब क्यों दिया गया। यह पूरी तरह से वैध है और इस मुद्दे को हल करता है।
लियोनिद उसोव

यह SwiftyJSON से Decodable
Michał Ziobro

यह तब हल नहीं करता है कि मेटाडेटा जसन को कैसे पार्स करें जो मूल समस्या थी।
लैलाकॉर्न

1

आपकी नजर बियोंवाजोन पर पड़ सकती है

import BeyovaJSON

struct Customer: Codable {
  let id: String
  let email: String
  let metadata: JToken
}

//create a customer instance

customer.metadata = ["link_id": "linked-id","buy_count": 4]

let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted 
print(String(bytes: try! encoder.encode(customer), encoding: .utf8)!)

ऊह, वास्तव में अच्छा। JToken के रूप में एक सामान्य JSON प्राप्त करने के लिए इसका उपयोग करना, कुछ मूल्यों को जोड़ना और सर्वर पर वापस आना। बहुत बढ़िया सचमुच। यह आपके द्वारा किया गया भयानक काम है :)
विक्टर ह्यूगो श्वाब

1

सबसे आसान और सुझाव दिया गया तरीका है कि हर शब्दकोश या मॉडल के लिए अलग मॉडल बनाया जाए जो JSON में हो

ये है जो मैं करता हूं

//Model for dictionary **Metadata**

struct Metadata: Codable {
    var link_id: String?
    var buy_count: Int?
}  

//Model for dictionary **Customer**

struct Customer: Codable {
   var object: String?
   var id: String?
   var email: String?
   var metadata: Metadata?
}

//Here is our decodable parser that decodes JSON into expected model

struct CustomerParser {
    var customer: Customer?
}

extension CustomerParser: Decodable {

//keys that matches exactly with JSON
enum CustomerKeys: String, CodingKey {
    case object = "object"
    case id = "id"
    case email = "email"
    case metadata = "metadata"
}

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CustomerKeys.self) // defining our (keyed) container

    let object: String = try container.decode(String.self, forKey: .object) // extracting the data
    let id: String = try container.decode(String.self, forKey: .id) // extracting the data
    let email: String = try container.decode(String.self, forKey: .email) // extracting the data

   //Here I have used metadata model instead of dictionary [String: Any]
    let metadata: Metadata = try container.decode(Metadata.self, forKey: .metadata) // extracting the data

    self.init(customer: Customer(object: object, id: id, email: email, metadata: metadata))

    }
}

उपयोग:

  if let url = Bundle.main.url(forResource: "customer-json-file", withExtension: "json") {
        do {
            let jsonData: Data =  try Data(contentsOf: url)
            let parser: CustomerParser = try JSONDecoder().decode(CustomerParser.self, from: jsonData)
            print(parser.customer ?? "null")

        } catch {

        }
    }

** मैंने पार्सिंग करते समय सुरक्षित पक्ष में वैकल्पिक का उपयोग किया है, आवश्यकतानुसार बदला जा सकता है।

इस विषय पर और अधिक पढ़ें


1
आपका उत्तर निश्चित रूप से स्विफ्ट 4.1 के लिए उपयुक्त है और आपकी पोस्ट की पहली पंक्ति मृत है! मान लें कि डेटा एक वेब सेवा से आ रहा है। आप सरल नेस्टेड वस्तुओं को मॉडल कर सकते हैं और फिर प्रत्येक को खींचने के लिए डॉट सिंटैक्स का उपयोग कर सकते हैं। नीचे देखें सुहित का जवाब
डेविड एच।

1

मैं एक फली रास्ता डिकोडिंग + एन्कोडिंग की सुविधा के लिए बनाया है [String: Any], [Any]। और यह वैकल्पिक गुणों को एन्कोड या डिकोड करता है, यहां https://github.com/levantAJ/AnyCodable है

pod 'DynamicCodable', '1.0'

इसे कैसे उपयोग करे:

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

0

यहाँ और अधिक सामान्य है (न केवल [String: Any], लेकिन[Any] डिकोड किया जा सकता है) और समझाया दृष्टिकोण (इसके लिए अलग इकाई का उपयोग किया जाता है) @loudmouth उत्तर से प्रेरित है।

इसका उपयोग करने की तरह दिखेगा:

extension Customer: Decodable {
  public init(from decoder: Decoder) throws {
    let selfContainer = try decoder.container(keyedBy: CodingKeys.self)
    id = try selfContainer.decode(.id)
    email = try selfContainer.decode(.email)
    let metadataContainer: JsonContainer = try selfContainer.decode(.metadata)
    guard let metadata = metadataContainer.value as? [String: Any] else {
      let context = DecodingError.Context(codingPath: [CodingKeys.metadata], debugDescription: "Expected '[String: Any]' for 'metadata' key")
      throw DecodingError.typeMismatch([String: Any].self, context)
    }
    self.metadata = metadata
  }

  private enum CodingKeys: String, CodingKey {
    case id, email, metadata
  }
}

JsonContainerएक सहायक इकाई है जिसका उपयोग हम JSON ऑब्जेक्ट को डिकोड करने के बिना JSON ऑब्जेक्ट (या तो सरणी या शब्दकोश) में लपेटने के लिए करते हैं *DecodingContainer(इसलिए यह JSON ऑब्जेक्ट द्वारा नहीं होने पर दुर्लभ मामलों में हस्तक्षेप नहीं करेगा [String: Any])।

struct JsonContainer {

  let value: Any
}

extension JsonContainer: Decodable {

  public init(from decoder: Decoder) throws {
    if let keyedContainer = try? decoder.container(keyedBy: Key.self) {
      var dictionary = [String: Any]()
      for key in keyedContainer.allKeys {
        if let value = try? keyedContainer.decode(Bool.self, forKey: key) {
          // Wrapping numeric and boolean types in `NSNumber` is important, so `as? Int64` or `as? Float` casts will work
          dictionary[key.stringValue] = NSNumber(value: value)
        } else if let value = try? keyedContainer.decode(Int64.self, forKey: key) {
          dictionary[key.stringValue] = NSNumber(value: value)
        } else if let value = try? keyedContainer.decode(Double.self, forKey: key) {
          dictionary[key.stringValue] = NSNumber(value: value)
        } else if let value = try? keyedContainer.decode(String.self, forKey: key) {
          dictionary[key.stringValue] = value
        } else if (try? keyedContainer.decodeNil(forKey: key)) ?? false {
          // NOP
        } else if let value = try? keyedContainer.decode(JsonContainer.self, forKey: key) {
          dictionary[key.stringValue] = value.value
        } else {
          throw DecodingError.dataCorruptedError(forKey: key, in: keyedContainer, debugDescription: "Unexpected value for \(key.stringValue) key")
        }
      }
      value = dictionary
    } else if var unkeyedContainer = try? decoder.unkeyedContainer() {
      var array = [Any]()
      while !unkeyedContainer.isAtEnd {
        let container = try unkeyedContainer.decode(JsonContainer.self)
        array.append(container.value)
      }
      value = array
    } else if let singleValueContainer = try? decoder.singleValueContainer() {
      if let value = try? singleValueContainer.decode(Bool.self) {
        self.value = NSNumber(value: value)
      } else if let value = try? singleValueContainer.decode(Int64.self) {
        self.value = NSNumber(value: value)
      } else if let value = try? singleValueContainer.decode(Double.self) {
        self.value = NSNumber(value: value)
      } else if let value = try? singleValueContainer.decode(String.self) {
        self.value = value
      } else if singleValueContainer.decodeNil() {
        value = NSNull()
      } else {
        throw DecodingError.dataCorruptedError(in: singleValueContainer, debugDescription: "Unexpected value")
      }
    } else {
      let context = DecodingError.Context(codingPath: [], debugDescription: "Invalid data format for JSON")
      throw DecodingError.dataCorrupted(context)
    }
  }

  private struct Key: CodingKey {
    var stringValue: String

    init?(stringValue: String) {
      self.stringValue = stringValue
    }

    var intValue: Int?

    init?(intValue: Int) {
      self.init(stringValue: "\(intValue)")
      self.intValue = intValue
    }
  }
}

ध्यान दें कि नंबर और बूलियन प्रकार द्वारा समर्थित हैं NSNumber, कुछ इस तरह से काम नहीं करेगा:

if customer.metadata["keyForInt"] as? Int64 { // as it always will be nil

क्या मैं केवल चुने हुए गुणों को डिकोड कर सकता हूं और अन्य डिकोड किए गए को स्वचालित रूप से छोड़ सकता हूं क्योंकि मेरे पास 15 गुण हैं जो ऑटो-कोडिंग और शायद 3 को पर्याप्त करते हैं जो कुछ कस्टम डिकोडिंग हैंडलिंग की आवश्यकता है?
मिचेल जाइब्रो

@ MichałZiobro क्या आप JSON ऑब्जेक्ट में डिकोड किए गए डेटा का हिस्सा चाहते हैं और इसका हिस्सा अलग-अलग इंस्टेंस वेरिएबल में डिकोड किया गया है? या आप केवल ऑब्जेक्ट के हिस्से के लिए आंशिक डिकोडिंग इनिशियलाइज़र लिखने के बारे में पूछ रहे हैं (और इसमें संरचना जैसी JSON के साथ कुछ भी सामान्य नहीं है)? मेरी जानकारी के लिए, पहले प्रश्न का उत्तर हां में है, दूसरे के लिए नहीं है।
एलेक्सी कोज़ेविकोव

मैं केवल कुछ गुणों को अनुकूलित डिकोडिंग के साथ और बाकी को मानक डिफ़ॉल्ट डिकोडिंग के साथ करना
चाहता हूं

@ MichałZiobro अगर मैं आपको सही समझता हूं तो यह संभव नहीं है। वैसे भी, आपका प्रश्न वर्तमान एसओ प्रश्न के लिए प्रासंगिक नहीं है और एक अलग मूल्य है।
एलेक्सी कोझ्वनिकोव

0

डिकोडर और कोडिंग कुंजी का उपयोग कर डीकोड

public let dataToDecode: [String: AnyDecodable]

enum CodingKeys: CodingKey {
    case dataToDecode
}

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.dataToDecode = try container.decode(Dictionary<String, AnyDecodable>.self, forKey: .dataToDecode) 
}    

-1
extension ViewController {

    func swiftyJson(){
        let url = URL(string: "https://itunes.apple.com/search?term=jack+johnson")
        //let url = URL(string: "http://makani.bitstaging.in/api/business/businesses_list")

        Alamofire.request(url!, method: .get, parameters: nil).responseJSON { response in
            var arrayIndexes = [IndexPath]()
            switch(response.result) {
            case .success(_):

                let data = response.result.value as! [String : Any]

                if let responseData =  Mapper<DataModel>().map(JSON: data) {
                    if responseData.results!.count > 0{
                        self.arrayExploreStylistList = []
                    }
                    for i in 0..<responseData.results!.count{
                        arrayIndexes.append(IndexPath(row: self.arrayExploreStylistList.count + i, section: 0))
                    }
                    self.arrayExploreStylistList.append(contentsOf: responseData.results!)

                    print(arrayIndexes.count)

                }

                //                    if let arrNew = data["results"] as? [[String : Any]]{
                //                        let jobData = Mapper<DataModel>().mapArray(JSONArray: arrNew)
                //                        print(jobData)
                //                        self.datamodel = jobData
                //                    }
                self.tblView.reloadData()
                break

            case .failure(_):
                print(response.result.error as Any)
                break

            }
        }

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