ChatGPT解决这个技术问题 Extra ChatGPT

如何在 Swift [45] 可解码协议中解码 JSON 字典类型的属性

假设我有 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 地图对象。

在我可以从 NSJSONDeserialization 的反序列化 JSON 但使用新的 Swift 4 Decodable 协议转换属性之前,我仍然想不出办法来做到这一点。

有谁知道如何在 Swift 4 中使用 Decodable 协议来实现这一点?


m
mfaani

在我发现的 this gist 的一些启发下,我为 UnkeyedDecodingContainerKeyedDecodingContainer 编写了一些扩展。您可以找到指向我的要点 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:如果您只是想将整个文件转换为字典,最好坚持使用 JSONSerialization 中的 api,因为我还没有想出一种方法来扩展 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
@PitiphongPhongpattranont 这段代码对你有用吗?
@JonBrooks UnkeyedDecodingContainerdecode(_ type: Array<Any>.Type) throws -> Array<Any> 中的最后一个条件是检查 nested 数组。因此,如果您有一个如下所示的数据结构: [true, 452.0, ["a", "b", "c"] ] 它将拉出嵌套的 ["a", "b", "c"] 数组。 UnkeyedDecodingContainerdecode 方法从容器中“弹出”元素。它不应该导致无限递归。
@loudmouth json 中的键可以有 nil 值:{"array": null}。因此,您的 guard contains(key) 将通过,但稍后在尝试解码键“array”的空值时会崩溃几行。所以最好在调用 decode 之前再添加一个条件来检查值是否实际上不为空。
我找到了解决方法:而不是 } 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) {
z
zoul

我也玩过这个问题,最后写了一个simple library for working with “generic JSON” types。 (其中“通用”的意思是“没有事先知道的结构”。)要点是用具体类型表示通用 JSON:

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

然后,此类型可以实现 CodableEquatable


这是一个非常优雅的解决方案。它非常简洁,效果很好,并且不像其他一些答案那样笨拙。我唯一的补充是将数字换成单独的整数和浮点类型。从技术上讲,所有数字在 JS 中都是浮点数,但在 swift 中将整数解码为整数更高效、更简洁。
S
Suhit Patil

您可以创建确认 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)
}

不,我不能,因为我不知道 metadata 值的结构。它可以是任意对象。
你的意思是它可以是数组或字典类型吗?
你能举个例子或添加更多关于元数据结构的解释吗
metadata 的值可以是任何 JSON 对象。所以它可以是空字典或任何字典。 “元数据”:{}“元数据”:{用户ID:“id”}“元数据”:{偏好:{显示值:真,语言:“en”}}等。
一种可能的选择是将元数据结构中的所有参数用作可选项,并列出元数据结构中的所有可能值,例如 struct metadata { var user_id: String?变量偏好:字符串? }
G
Giuseppe Lanza

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

假设我们要解析的不仅仅是一个简单的 [String: Any],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)

这将如何解码数组?
P
Pitiphong Phongpattranont

当我找到旧答案时,我只测试了一个简单的 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 登陆 Swift,这将解决这个问题,或者至少帮助它变得更好。

[旧答案]

我在 Apple Developer 论坛上发布了这个问题,事实证明这很容易。

我可以

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

在初始化程序中。

一开始就错过了那是我的错。


可以在 Apple Developer 上发布问题的链接。 Any 不符合 Decodable,所以我不确定这是正确的答案。
@RezaShirazian 这就是我最初的想法。但事实证明,当 Dictionary 的键符合 Hashable 且不依赖于其值时,Dictionary 符合 Encodable。您可以打开字典标题并自己查看。扩展字典:可编码其中键:可散列扩展字典:可解码其中键:可散列forums.developer.apple.com/thread/80288#237680
目前这不起作用。 “Dictionary 不符合 Decodable,因为 Any 不符合 Decodable”
事实证明它有效。我在我的代码中使用它。您需要了解,现在没有办法表达“字典的值必须符合可解码协议才能使字典符合可解码协议”的要求。那是尚未在 Swift 4 中实现的“条件一致性”,我认为现在还可以,因为 Swift 类型系统(和泛型)有很多限制。所以这暂时有效,但是当 Swift 类型系统将来改进时(特别是在实现条件一致性时),这不应该有效。
从 Xcode 9 beta 5 开始对我不起作用。编译,但在运行时崩溃: Dictionary 不符合 Decodable 因为 Any 不符合 Decodable。
a
allen huang

如果您使用 SwiftyJSON 解析 JSON,您可以更新到具有 Codable 协议支持的 4.1.0。只需声明 metadata: JSON 就可以了。

import SwiftyJSON

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

我不知道为什么这个答案被否决了。它完全有效并解决了问题。
从 SwiftyJSON 迁移到 Decodable 似乎很好
这并没有解决如何解析作为原始问题的元数据 json。
k
koen

我写了一篇文章和 repo,帮助添加 [String: Any] 对 Codable 的解码和编码支持。

https://medium.com/nerd-for-tech/string-any-support-for-codable-4ba062ce62f2

这改进了可解码方面,还添加了可编码支持作为 https://stackoverflow.com/a/46049763/9160905 中给出的解决方案

您将能够实现的目标:

json:

https://i.stack.imgur.com/7TVwt.png

示例代码:

https://i.stack.imgur.com/HK5zX.png


c
canius

您可以看看 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)!)

哦,真不错。使用它来接收作为 JToken 的通用 JSON,附加一些值并返回到服务器。确实非常好。那是你所做的很棒的工作:)
A
Alexey Kozhevnikov

这是受@loudmouth 答案启发的更通用(不仅[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 的辅助实体(因此它不会干扰 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 个属性足以满足 autoDecoding 的需求,也许 3 个属性需要一些自定义解码处理?
@MichałZiobro 您是否希望将部分数据解码为 JSON 对象,并将部分数据解码为单独的实例变量?或者您正在询问仅为对象的一部分编写部分解码初始化程序(并且它与类似 JSON 的结构没有任何共同点)?据我所知,第一个问题的答案是肯定的,第二个问题的答案是否定的。
我希望只有一些属性具有自定义解码,其余属性具有标准默认解码
@MichałZiobro 如果我理解正确,那是不可能的。无论如何,您的问题与当前的 SO 问题无关,值得单独提出一个问题。
T
Tai Le

我做了一个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)
    }
}

V
Vasily Bodnarchuk

细节

Xcode 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

A
Ashim Dahal

使用解码器和编码键进行解码

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

您需要定义 AnyDecodable
T
Tassio Marques

我使用了有关该主题的一些答案来为我获得最简单的解决方案。我的问题是我收到了一个 [String: Any] 类型的字典,但我可以很好地使用 [String: String] 转换字符串中的每个其他 Any 值。所以这是我的解决方案:

struct MetadataType: Codable {
    let value: String?

    private init(_ value: String?) {
        self.value = value
    }

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

        if let decodedValue = try? container.decode(Int.self) {
            self.init(String(decodedValue))
        } else if let decodedValue = try? container.decode(Double.self) {
            self.init(String(decodedValue))
        } else if let decodedValue = try? container.decode(Bool.self) {
            self.init(String(decodedValue))
        } else if let decodedValue = try? container.decode(String.self) {
            self.init(decodedValue)
        } else {
            self.init(nil)
        }
    }
}

在声明我的字典时,我使用

let userInfo: [String: MetadataType]

m
minhazur

最简单和建议的方法是为 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 {

        }
    }

**我在解析时使用了 optional 以确保安全,可以根据需要进行更改。

Read more on this topic


你的答案肯定适合 Swift 4.1,而且你帖子的第一行已经死了!假设数据来自 Web 服务。您可以对简单的嵌套对象进行建模,然后使用点语法来抓取每个对象。请参阅下面的 suhit 答案。
M
Mahendra Y

这将起作用

public struct AnyDecodable: Decodable {
    public let value: Any

    public init<T>(_ value: T?) {
        self.value = value ?? ()
    }
}

let contentDecodable = try values.decodeIfPresent(AnyDecodable.self, forKey: .content)

AnyDecodable 不符合 Decodable
p
pkamb
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

            }
        }

    }
}