swift 如何快速实现结构体的默认可解码?

czq61nw1  于 2022-12-17  发布在  Swift
关注(0)|答案(2)|浏览(161)
struct Person: Decodable {
    let firstName: String
}
var data = """
{"firstName": "Fai"}
""".data(using: .utf8)!
let decoder = JSONDecoder()
let parsed = try decoder.decode(Person.self, from: data)

JSONDecoder将对数据进行解码,该数据符合Decodable协议。
所以我想知道如何快速实现这一点。但我不能得到任何想法的源代码:https://github.com/apple/swift/blob/main/stdlib/public/core/Codable.swift
Decodable协议只需要实现一个init(from decoder: Decoder)函数,如果要做的话,我会对struct做一个扩展:

extension struct: Decodable {
  init(from decoder: Decoder) {...}
}

但是当我删除示例中的Decodable时,编译器会给予错误:
示例方法“decode(_:from:)”要求“Person”符合“Decodable”
所以这不是实现这个的快捷方式。快捷方式怎么样?源代码在哪里?

ff29svar

ff29svar1#

您可以看到编译器使用-print-ast为您编写的内容:

echo 'struct Person: Decodable {
    let firstName: String
}' | swiftc -print-ast -

这将输出大部分自动生成的代码(它应该包括所有的Codable一致性,但是还有一些其他类型的自动生成代码不包括它们的实现):

internal struct Person : Decodable {
  internal let firstName: String
  private enum CodingKeys : CodingKey {
    case firstName
    @_implements(Equatable, ==(_:_:)) fileprivate static func __derived_enum_equals(_ a: Person.CodingKeys, _ b: Person.CodingKeys) -> Bool {
      private var index_a: Int
      switch a {
      case .firstName:

        index_a = 0
      }
      private var index_b: Int
      switch b {
      case .firstName:

        index_b = 0
      }
      return index_a == index_b
    }
    fileprivate func hash(into hasher: inout Hasher) {
      private var discriminator: Int
      switch self {
      case .firstName:

        discriminator = 0
      }
      hasher.combine(discriminator)
    }
    private init?(stringValue: String) {
      switch stringValue {
      case "firstName":

        self = Person.CodingKeys.firstName
        default:

        return nil
      }

    }
    private init?(intValue: Int) {
      return nil
    }
    fileprivate var hashValue: Int {
      get {
        return _hashValue(for: self)
      }
    }
    fileprivate var intValue: Int? {
      get {
        return nil
      }
    }
    fileprivate var stringValue: String {
      get {
        switch self {
        case .firstName:

          return "firstName"
        }
      }
    }
  }
  internal init(firstName: String)
  internal init(from decoder: Decoder) throws {
    @_hasInitialValue private let container: KeyedDecodingContainer<Person.CodingKeys> = try decoder.container(keyedBy: Person.CodingKeys.self)

    self.firstName = try container.decode(String.self, forKey: Person.CodingKeys.firstName)

  }
}

有关完整的实现细节,请参见DerivedConformanceCodable.cpp。您最感兴趣的问题可能是deriveBodyDecodable_init

nxagd54h

nxagd54h2#

Swift会自动为没有自定义实现init(from:)初始化器的结构体合成Decodable协议,这意味着你可以使用一个结构体作为Decodable类型,而无需自己实现init(from:)初始化器,只要该结构体的属性与编码数据中的键匹配。
例如,假设您有以下结构:

struct User: Decodable {
    let id: Int
    let name: String
    let email: String
}

这个结构体符合Decodable协议,因为它没有init(from:)初始化器的自定义实现。当您尝试解码包含密钥id、name和email的数据时,Swift将自动使用User结构体的属性名称和类型来解码数据并创建一个新的User示例。
您可以使用JSONDecoder类的decode(_:from:)方法对数据进行解码,如下所示:

let json = """
{
    "id": 123,
    "name": "John Doe",
    "email": "john.doe@example.com"
}
"""

let decoder = JSONDecoder()
let user = try decoder.decode(User.self, from: json.data(using: .utf8)!)

相关问题