如何从Swift 4的Codable中排除属性

Ram*_*att 68 json swift codable

Swfit 4的新Encodable/ Decodable协议使JSON(de)序列化非常令人愉快.但是,我还没有找到一种方法可以对哪些属性进行编码以及应该解码哪些属性进行细粒度控制.

我注意到从附带的CodingKeysenum中排除属性完全排除了该属性,但有没有办法进行更细粒度的控制?

Cod*_*ent 131

编码/解码的键列表由一个名为的类型控制CodingKeys.编译器可以为您合成这个,但总是可以覆盖它.

假设您不想对属性进行编码/解码s:

struct Person: Codable {
    var firstName: String
    var lastName: String
    var nickname: String?

    private enum CodingKeys: String, CodingKey {
        case firstName, lastName
    }
}
Run Code Online (Sandbox Code Playgroud)

这将排除nickname编码解码.


如果您希望它是非对称的(即编码但不解码,反之亦然),您必须提供自己的实现encode(with encoder: )init(from decoder: ):

struct Person: Codable {
    var firstName: String
    var lastName: String

    // Since fullName is a computed property, it's excluded by default
    var fullName: String {
        return firstName + " " + lastName
    }

    private enum CodingKeys: String, CodingKey {
        case firstName
        case lastName
        case fullName
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        firstName = try container.decode(String.self, forKey: .firstName)
        lastName = try container.decode(String.self, forKey: .lastName)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(firstName, forKey: .firstName)
        try container.encode(lastName, forKey: .lastName)
        try container.encode(fullName, forKey: .fullName)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 你需要给"昵称"一个默认值才能生效.否则,没有值可以在`init(from:)`上分配给属性. (14认同)
  • @ItaiFerber 我将它切换为可选的,它最初在我的 Xcode 中 (2认同)
  • 刚刚在 Swift 5 中测试了这一点。您应该只需要为不解码的属性定义一个常量。您不需要将密钥显式添加到“CodingKeys”。所以, `var昵称:String { get { "name" } }` 应该足够了。 (2认同)

小智 46

使用自定义属性包装器的解决方案

struct Person: Codable {
    var firstName: String
    var lastName: String
    
    @CodableIgnored
    var nickname: String?
}
Run Code Online (Sandbox Code Playgroud)

哪里CodableIgnored

@propertyWrapper
public struct CodableIgnored<T>: Codable {
    public var wrappedValue: T?
        
    public init(wrappedValue: T?) {
        self.wrappedValue = wrappedValue
    }
    
    public init(from decoder: Decoder) throws {
        self.wrappedValue = nil
    }
    
    public func encode(to encoder: Encoder) throws {
        // Do nothing
    }
}

extension KeyedDecodingContainer {
    public func decode<T>(
        _ type: CodableIgnored<T>.Type,
        forKey key: Self.Key) throws -> CodableIgnored<T>
    {
        return CodableIgnored(wrappedValue: nil)
    }
}

extension KeyedEncodingContainer {
    public mutating func encode<T>(
        _ value: CodableIgnored<T>,
        forKey key: KeyedEncodingContainer<K>.Key) throws
    {
        // Do nothing
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这似乎完全适合您希望几乎所有东西都使用 Codability 的默认实现的情况,除了一两个成员之外。引入整个自定义 Codable 一致性的复杂性将是一种耻辱,而这可以通过将复杂性封装在其他地方并使使用站点适当最小化来防止这种情况发生。属性包装器 FTW (2认同)

Hri*_*are 5

如果我们需要从结构中的大量属性中排除几个属性的解码,请将它们声明为可选属性。解开选项的代码比在 CodingKey 枚举下编写大量键要少。

我建议使用扩展来添加计算实例属性和计算类型属性。它将可编码的一致性属性与其他逻辑分开,因此提供了更好的可读性。


小智 5

另一种从编码器中排除某些属性的方法,可以使用单独的编码容器

struct Person: Codable {
    let firstName: String
    let lastName: String
    let excludedFromEncoder: String

    private enum CodingKeys: String, CodingKey {
        case firstName
        case lastName
    }
    private enum AdditionalCodingKeys: String, CodingKey {
        case excludedFromEncoder
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let anotherContainer = try decoder.container(keyedBy: AdditionalCodingKeys.self)
        firstName = try container.decode(String.self, forKey: .firstName)
        lastName = try container.decode(String.self, forKey: .lastName)

        excludedFromEncoder = try anotherContainer(String.self, forKey: . excludedFromEncoder)
    }

    // it is not necessary to implement custom encoding
    // func encode(to encoder: Encoder) throws

    // let person = Person(firstName: "fname", lastName: "lname", excludedFromEncoder: "only for decoding")
    // let jsonData = try JSONEncoder().encode(person)
    // let jsonString = String(data: jsonData, encoding: .utf8)
    // jsonString --> {"firstName": "fname", "lastName": "lname"}

}
Run Code Online (Sandbox Code Playgroud)

相同的方法可用于解码器