代码之家  ›  专栏  ›  技术社区  ›  justintime Sam

在Swift字典中存储编码

  •  2
  • justintime Sam  · 技术社区  · 7 年前

    我希望在字典中存储模型对象,并希望使用 JSONEncoder 转换为数据,然后转换为字符串并保存。

    想法是使用现成的Swift 4 Encodable 为了确保我添加到字典中的任何内容都将被序列化,其中可以包括原语和自定义对象(它们本身符合 可编码的 ).

    问题是我应该将字典声明为什么类型:

    • 如果我使用 [String: Any] ,它不知道如何编码 Any ,如果我必须将其转换为实际的具体类型,这有点违背泛型的目的
    • 如果我使用 [String: Encodable] ,它将在运行时崩溃 Encodable不符合自身,这是可以理解的 需要混凝土类型

    为了解决这个问题,我考虑创建一个包装器: i、 e具有关联类型的协议 具有泛型类型值的结构:

    struct Serializable<T: Encodable> {
        var value: T?
    
        init(value: T) {
           self.value = value
        }
    }
    

    但问题仍然存在,在声明上述字典的类型时,我仍然必须提供具体的类型。。

    var dictionary: [String: Serializable<X>]
    

    “X”应该是什么,或者,实现这一点的正确方法是什么? 我错过了什么?

    2 回复  |  直到 7 年前
        1
  •  2
  •   Rob Md Fahim Faez Abir    7 年前

    两种可能的方法:

    1. 您可以创建字典,其值为 Encodable 仅对基础值进行编码的包装器类型:

      struct EncodableValue: Encodable {
          let value: Encodable
      
          func encode(to encoder: Encoder) throws {
              try value.encode(to: encoder)
          }
      }
      

      然后您可以执行以下操作:

      let dictionary = [
          "foo": EncodableValue(value: Foo(string: "Hello, world!")),
          "bar": EncodableValue(value: Bar(value: 42)),
          "baz": EncodableValue(value: "qux")
      ]
      
      let data = try! JSONEncoder().encode(dictionary)
      
    2. 您可以定义自己的 Codable 键入而不是使用字典:

      struct RequestObject: Encodable {
          let foo: Foo
          let bar: Bar
          let baz: String
      }
      
      let requestObject = RequestObject(
          foo: Foo(string: "Hello, world!"), 
          bar: Bar(value: 42),
          baz: "qux"
      )
      
      let data = try! JSONEncoder().encode(requestObject)
      

    不用说,这两者都假设 Foo Bar 符合 可编码的 .

        2
  •  0
  •   Rob Md Fahim Faez Abir    7 年前

    这是我的解决方案(由Rob answer改进):

    struct EncodableValue: Encodable {
        let value: Encodable
    
        func encode(to encoder: Encoder) throws {
            try value.encode(to: encoder)
        }
    }
    
    struct Storage: Encodable {
        var dict: [String: Encodable] = [:]
        func encode(to encoder: Encoder) throws {
            var container = encoder.container(keyedBy: CodingKeys.self)
            for (key, value) in dict {
                guard let codingKey = CodingKeys(stringValue: key) else {
                    continue
                }
                if let enc = value as? EncodableValue {
                    try container.encode(enc, forKey: codingKey)
                }
            }
        }
    
        struct CodingKeys: CodingKey {
            var stringValue: String
            init?(stringValue: String) {
                self.stringValue = stringValue
            }
            var intValue: Int?
            init?(intValue: Int) {
                return nil
            }
        }
    }
    
    let dict: [String: EncodableValue] = ["test": EncodableValue(value:1), "abc":EncodableValue(value:"GOGO")]
    let storage = Storage(dict: dict)
    
    do {
        let data = try JSONEncoder().encode(storage)
        let res = String(data: data, encoding: .utf8)
        print(res ?? "nil")
    } catch {
        print(error)
    }
    
    推荐文章