代码之家  ›  专栏  ›  技术社区  ›  Shashank

Swift 4 JSON解码[String:Any][duplicate]

  •  0
  • Shashank  · 技术社区  · 6 年前

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

    这个 属性可以是任意JSON映射对象。

    在我可以从 NSJSONDeserialization 但是新的雨燕4 Decodable 礼节,我还是想不出办法。

    有人知道如何用可解码协议在Swift 4中实现这一点吗?

    0 回复  |  直到 4 年前
        1
  •  109
  •   mfaani    6 年前

    灵感来自 this gist UnkeyedDecodingContainer KeyedDecodingContainer . 你可以找到我要点的链接 here 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:

    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)
        }
    }
    
        2
  •  28
  •   zoul    7 年前

    simple library for working with “generic JSON” types . (其中generic表示没有预先知道的结构。)要点是用具体类型表示generic JSON:

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

    然后可以实现这种类型 Codable Equatable .

        3
  •  16
  •   Suhit Patil    4 年前

    您可以创建确认 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)
    }
    
        4
  •  8
  •   Pitiphong Phongpattranont    7 年前

    当我找到旧的答案时,我只测试了一个简单的JSON对象案例,但没有测试一个空的案例,它会导致运行时异常,比如@slurmatic和@zoul found。很抱歉这个问题。

    AnyJSONValue 键入erasure struct并使用该类型而不是 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)
    

    在初始值设定项中。

    一开始我就错过了,这是我的错。

        5
  •  8
  •   Giuseppe Lanza    7 年前

    我提出了一个稍微不同的解决方案。

    假设我们有比简单的 [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
  •  7
  •   allen huang    6 年前

    SwiftyJSON 要解析JSON,可以更新到 4.1.0 哪个有 Codable 协议支持。申报就行了 metadata: JSON 你们都准备好了。

    import SwiftyJSON
    
    struct Customer {
      let id: String
      let email: String
      let metadata: JSON
    }
    
        7
  •  1
  •   canius    7 年前

    你可以看看 BeyovaJSON

    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)!)
    
        8
  •  1
  •   Tai Le    6 年前

    我做了一个pod,以方便解码+编码的方式 [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)
        }
    }
    
        9
  •  0
  •   minhazur ak93    7 年前

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

    **我用可选的是在安全的一面进行解析,可以根据需要进行更改。

    Read more on this topic

        10
  •  0
  •   Alexey Kozhevnikov    7 年前

    这里是更通用的(不仅仅是 [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 是一个helper实体,用于将解码的JSON数据包装到JSON对象(数组或字典),而无需扩展 *DecodingContainer (因此,当JSON对象不是 ).

    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
    
        11
  •  0
  •   Ashim Dahal    4 年前

    使用解码器和编码键解码

    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) 
    }    
    
        12
  •  0
  •   Vasily Bodnarchuk    4 年前

    细节

    • Xcode 12.0.1(12A7300)
    • Swift 5.3版

    基于 Tai Le 图书馆

    // 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
    
        13
  •  -1
  •   pkamb santoshIOS    5 年前
    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
    
                }
            }
    
        }
    }