সুইফট [45] ডিকোডেবল প্রোটোকলে প্রকারের JSON অভিধান সহ কোনও সম্পত্তি কীভাবে ডিকোড করবেন


114

ধরা যাক আমার কাছে Customerডেটা টাইপ রয়েছে যাতে এমন একটি metadataসম্পত্তি রয়েছে যা গ্রাহক অবজেক্টে কোনও জেএসএন অভিধান থাকতে পারে

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 মানচিত্র বস্তুর হতে পারে।

আমি ডিসরিয়ালাইজড জেএসওন থেকে সম্পত্তিটি কাস্ট করতে পারার আগে NSJSONDeserializationকিন্তু নতুন সুইফট 4 Decodableপ্রোটোকলের সাহায্যে, এখনও আমি এটি করার উপায় সম্পর্কে ভাবতে পারি না।

ডিকোডেবল প্রোটোকলের সাহায্যে কেউ কীভাবে সুইফট 4 এ এটি অর্জন করবেন জানেন?

উত্তর:


96

আমি এই উদ্ধৃতি থেকে কিছু অনুপ্রেরণা পেয়েছি, আমি 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]]

সম্পাদনা 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)
    }
}

আকর্ষণীয়, আমি এই টুকরোটি চেষ্টা করব এবং ফলাফলটি আপনার কাছে আপডেট করব @ লাউডমাউথ
পিটিফং ফোংপ্যাট্রান্ট

পিটিফংফোনপ্যাট্রান্ট এই কোডটি আপনার জন্য কার্যকর হয়েছে?
লাউডমাউথ

4
মধ্যে গত শর্ত @JonBrooks UnkeyedDecodingContainer's decode(_ type: Array<Any>.Type) throws -> Array<Any>একটি জন্য চেক করা হয় নেস্টেড অ্যারে। সুতরাং আপনার যদি এমন কোনও ডেটা কাঠামো থাকে যা নীচের মত দেখাচ্ছে: [true, 452.0, ["a", "b", "c"] ] এটি নেস্টেড ["a", "b", "c"]অ্যারে টানবে । decodeএকটি পদ্ধতি UnkeyedDecodingContainerধারক থেকে উপাদান বন্ধ "পপ"। এটি অসীম পুনরাবৃত্তি হওয়া উচিত নয়।
লাউডমাউথ

4
@loudmouth এটা JSON মধ্যে কী জন্য Nil মান করা সম্ভব: {"array": null}। সুতরাং আপনার guard contains(key)উইল পাস হবে তবে কী "অ্যারে" এর নাল মানকে ডিকোড করার চেষ্টা করার পরে এটি কয়েকটি লাইন ক্র্যাশ করবে। সুতরাং কল করার আগে মানটি আসলে নাল নয় কিনা তা যাচাই করার জন্য আরও একটি শর্ত যুক্ত করা ভাল decode
শেবুর

4
আমি একটি সমাধান পেয়েছি: পরিবর্তে } 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

আমিও এই সমস্যার সাথে খেলেছি এবং অবশেষে "জেনেরিক জেএসওএন" প্রকারের সাথে কাজ করার জন্য একটি সাধারণ লাইব্রেরি লিখেছি । (যেখানে "জেনেরিক" এর অর্থ "কোনও কাঠামো আগেই জানা নেই")

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

12
না আমি পারি না, যেহেতু আমি metadataমানটির কাঠামো জানি না । এটি যেকোন স্বেচ্ছাসেবী বস্তু হতে পারে।
পিটিফং ফোংপ্যাট্রান্ট

আপনার অর্থ কি এটি অ্যারে বা অভিধানের ধরণ হতে পারে?
সুহিত পাতিল

আপনি উদাহরণ দিতে পারেন বা
মেটাটাটা

4
মানটি metadataকোনও জেএসওএন অবজেক্ট হতে পারে। সুতরাং এটি খালি অভিধান বা কোনও অভিধান হতে পারে। "মেটাডেটা": {} "মেটাডেটা": {USER_ID: "ID"} "মেটাডেটা": {পক্ষপাত: {shows_value: সত্য, ভাষা: "স্বীকারোক্তি"}} ইত্যাদি
Pitiphong Phongpattranont

একটি সম্ভাব্য বিকল্প হ'ল মেটাডাটা স্ট্রাক্টের সমস্ত প্যারামগুলি বিকল্প হিসাবে ব্যবহার করা এবং স্ট্রাক্ট মেটাডেটা যেমন ব্যবহারকারীর ID: স্ট্রিংয়ের মতো মেটাডেটা স্ট্রাক্টে সমস্ত সম্ভাব্য মান তালিকাভুক্ত করা? var পছন্দ: স্ট্রিং? }
সুহিত পাতিল

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)

7

আমি যখন পুরানো উত্তরটি পেয়েছি তখন আমি কেবল একটি সাধারণ জেএসএন অবজেক্ট কেসটি পরীক্ষা করেছি তবে একটি খালি নয় যা @ স্লর্মোম্যাটিক এবং @ জুলের মতো রানটাইম ব্যতিক্রম ঘটায়। এই সমস্যাটির জন্য দুঃখিত।

সুতরাং আমি একটি সাধারণ 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সুইফটে অবতরণ হওয়া পর্যন্ত অপেক্ষা করা দরকার , এটি এই সমস্যার সমাধান করবে বা কমপক্ষে এটি আরও ভাল হতে সহায়তা করবে।


[পুরানো উত্তর]

আমি এই প্রশ্নটি অ্যাপল বিকাশকারী ফোরামে পোস্ট করেছি এবং এটি খুব সহজেই দেখা যাচ্ছে।

আমি করতে পারি

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

ইনিশিয়ালাইজারে।

প্রথম স্থানটি মিস করা আমার খারাপ ছিল।


4
অ্যাপল বিকাশকারীকে প্রশ্নের লিঙ্কটি পোস্ট করতে পারে। Anyমেনে চলছে না Decodableসুতরাং আমি নিশ্চিত নই কিভাবে এটি সঠিক উত্তর।
রেজা শিরাজিয়ান

@ রেজাশিরাজিয়ান এটাই আমি প্রথম স্থানে ভেবেছিলাম। তবে দেখা যাচ্ছে যে অভিধানগুলি এনকোডেবলের সাথে সঙ্গতি রাখে যখন এর কীগুলি হ্যাশেবলের কাছে মেনে চলে এবং মানগুলির উপর নির্ভর করে না। আপনি অভিধানের শিরোনামটি খুলতে পারেন এবং এটি নিজে থেকে দেখতে পারেন। এক্সটেনশন অভিধান: Encodable যেখানে কী: Hashable এক্সটেনশন অভিধান: Decodable যেখানে কী: Hashable forums.developer.apple.com/thread/80288#237680
Pitiphong Phongpattranont

6
বর্তমানে এটি কাজ করে না "অভিধান <স্ট্রিং, যে কোনও> ডিকোডেবলের সাথে সঙ্গতি রাখে না কারণ কোনওটি ডিকোডেবলের সাথে সম্মতি দেয় না"
এমবুচেটিক্স

এটি কাজ করে। আমি আমার কোড এ ব্যবহার করছি। আপনার বুঝতে হবে যে "ডিকোডেবল প্রোটোকলটিকে এখন ডিকোডেবল প্রোটোকল অনুসারে অভিধান তৈরি করতে ডিকোডেবল প্রোটোকলের সাথে অবশ্যই অভিধানের মান অবশ্যই মেনে চলবে" এই প্রয়োজনটি প্রকাশ করার কোনও উপায় নেই। এটি "শর্তসাপেক্ষ কনফারেন্স" যা সুইফট 4 এ এখনও কার্যকর করা হয়নি বলে আমি মনে করি এটি আপাতত ঠিক আছে যেহেতু সুইফট টাইপ সিস্টেমে (এবং জেনারিক্স) প্রচুর সীমাবদ্ধতা রয়েছে। সুতরাং এটি আপাতত কাজ করে তবে ভবিষ্যতে যখন সুইফট টাইপ সিস্টেমের উন্নতি হয় (বিশেষত শর্তসাপেক্ষ কনফারেন্স বাস্তবায়িত হয়) তখন এটি কাজ করা উচিত নয়।
পিটিফং ফোংপ্যাট্রান্ট

4
এক্সকোড 9 বিটা 5 হিসাবে আমার জন্য কাজ করে না Comp
জুল

6

আপনি JSON পার্স করার জন্য যদি SwiftyJSON ব্যবহার করেন তবে আপনি 4.1.0 এ আপডেট করতে পারবেন যার Codableপ্রোটোকল সমর্থন রয়েছে। শুধু ঘোষণা করুন metadata: JSONএবং আপনি প্রস্তুত।

import SwiftyJSON

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

আমি জানি না কেন এই উত্তরটি হ্রাস পেয়েছিল। এটি সম্পূর্ণ বৈধ এবং সমস্যা সমাধান করে।
লিওনিড উসোভ

এটা তোলে SwiftyJSON থেকে Decodable অভিবাসনের জন্য ভাল হবে বলে মনে হয়
মাইকেল Ziobro

মূল সমস্যাটি কীভাবে মেটাটাটা জেএসনকে পার্স করতে হয় তা সমাধান করে না।
21:25

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

ওহ, সত্যিই দুর্দান্ত। জেটোকেন হিসাবে জেনেরিক জেএসওএন গ্রহণ করতে এটি ব্যবহার করে কিছু মান যুক্ত করে সার্ভারে ফিরে আসা। অবশ্যই খুব ভাল. এটি আপনার দ্বারা দুর্দান্ত কাজ করা হয়েছে :)
ভিজিটর হুগো শোয়াব

1

আমি একটি শুঁটি পথ পাঠোদ্ধারতা + + এনকোডিং সহজতর করেছেন [String: Any], [Any]। এবং এটি এনকোড প্রদান করে বা alচ্ছিক বৈশিষ্ট্যগুলি ডিকোড করে এখানে 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

সবচেয়ে সহজ এবং প্রস্তাবিত উপায় হ'ল জেএসএনে থাকা প্রতিটি অভিধান বা মডেলের জন্য পৃথক মডেল তৈরি করা

এখানে আমি কি করি

//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 {

        }
    }

** পার্স করার সময় আমি নিরাপদ পাশে থাকতে alচ্ছিক ব্যবহার করেছি, প্রয়োজন অনুযায়ী পরিবর্তন করা যেতে পারে।

এই বিষয়ে আরও পড়ুন


4
আপনার উত্তরটি অবশ্যই সুইফট ৪.১ এর জন্য উপযুক্ত এবং আপনার পোস্টের প্রথম লাইনটি মারা গেছে! ধরে নিচ্ছি ডেটা একটি ওয়েব পরিষেবা থেকে আসছে। আপনি সাধারণ নেস্টেড অবজেক্টগুলিকে মডেল করতে পারেন তারপরে প্রতিটি দখলের জন্য ডট সিনট্যাক্স ব্যবহার করুন। নীচে সুহিতের উত্তর দেখুন।
ডেভিড এইচ

0

এখানে আরও জেনেরিক (কেবল নয় [String: Any], তবে [Any]ডিকোড করা যাবে) এবং এনক্যাপসুলেটেড অ্যাপ্রোচ (তার জন্য পৃথক সত্তা ব্যবহৃত হয়) @ লাউডমাউথ উত্তরের দ্বারা অনুপ্রাণিত।

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

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(যাতে কোনও জেএসওএন অবজেক্ট দ্বারা বোঝানো না হলে এটি বিরল ক্ষেত্রে হস্তক্ষেপ করবে না [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

@ মাইচাজিওব্রো যদি আমি আপনাকে সঠিকভাবে বুঝতে পারি তবে এটি সম্ভব নয়। যাইহোক, আপনার প্রশ্নটি বর্তমান এসও প্রশ্নের সাথে প্রাসঙ্গিক নয় এবং পৃথক প্রশ্নের মূল্য।
অ্যালেক্সি কোজেভনিকভ

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

0

বিশদ

  • এক্সকোড 12.0.1 (12A7300)
  • সুইফ্ট 5.3

তাই লে লাইব্রেরির উপর ভিত্তি করে

// code from: https://github.com/levantAJ/AnyCodable/blob/master/AnyCodable/DecodingContainer%2BAnyCollection.swift

private
struct AnyCodingKey: CodingKey {
    let stringValue: String
    private (set) var intValue: Int?
    init?(stringValue: String) { self.stringValue = stringValue }
    init?(intValue: Int) {
        self.intValue = intValue
        stringValue = String(intValue)
    }
}

extension KeyedDecodingContainer {

    private
    func decode(_ type: [Any].Type, forKey key: KeyedDecodingContainer<K>.Key) throws -> [Any] {
        var values = try nestedUnkeyedContainer(forKey: key)
        return try values.decode(type)
    }

    private
    func decode(_ type: [String: Any].Type, forKey key: KeyedDecodingContainer<K>.Key) throws -> [String: Any] {
        try nestedContainer(keyedBy: AnyCodingKey.self, forKey: key).decode(type)
    }

    func decode(_ type: [String: Any].Type) throws -> [String: Any] {
        var dictionary: [String: Any] = [:]
        for key in allKeys {
            if try decodeNil(forKey: key) {
                dictionary[key.stringValue] = NSNull()
            } else if let bool = try? decode(Bool.self, forKey: key) {
                dictionary[key.stringValue] = bool
            } else if let string = try? decode(String.self, forKey: key) {
                dictionary[key.stringValue] = string
            } else if let int = try? decode(Int.self, forKey: key) {
                dictionary[key.stringValue] = int
            } else if let double = try? decode(Double.self, forKey: key) {
                dictionary[key.stringValue] = double
            } else if let dict = try? decode([String: Any].self, forKey: key) {
                dictionary[key.stringValue] = dict
            } else if let array = try? decode([Any].self, forKey: key) {
                dictionary[key.stringValue] = array
            }
        }
        return dictionary
    }
}

extension UnkeyedDecodingContainer {
    mutating func decode(_ type: [Any].Type) throws -> [Any] {
        var elements: [Any] = []
        while !isAtEnd {
            if try decodeNil() {
                elements.append(NSNull())
            } else if let int = try? decode(Int.self) {
                elements.append(int)
            } else if let bool = try? decode(Bool.self) {
                elements.append(bool)
            } else if let double = try? decode(Double.self) {
                elements.append(double)
            } else if let string = try? decode(String.self) {
                elements.append(string)
            } else if let values = try? nestedContainer(keyedBy: AnyCodingKey.self),
                let element = try? values.decode([String: Any].self) {
                elements.append(element)
            } else if var values = try? nestedUnkeyedContainer(),
                let element = try? values.decode([Any].self) {
                elements.append(element)
            }
        }
        return elements
    }
}

সমাধান

struct DecodableDictionary: Decodable {
    typealias Value = [String: Any]
    let dictionary: Value?
    init(from decoder: Decoder) throws {
        dictionary = try? decoder.container(keyedBy: AnyCodingKey.self).decode(Value.self)
    }
}

ব্যবহার

struct Model: Decodable {
    let num: Double?
    let flag: Bool?
    let dict: DecodableDictionary?
    let dict2: DecodableDictionary?
    let dict3: DecodableDictionary?
}

let data = try! JSONSerialization.data(withJSONObject: dictionary)
let object = try JSONDecoder().decode(Model.self, from: data)
print(object.dict?.dictionary)      // prints [String: Any]
print(object.dict2?.dictionary)     // prints nil
print(object.dict3?.dictionary)     // prints nil

-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

            }
        }

    }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.