Swift 4的新的Encodable
/Decodable
协议使JSON(反)序列化变得非常令人愉快。但是,我还没有找到一种方法可以对应该编码的属性和应该解码的属性进行细粒度控制。
我注意到,从随附的CodingKeys
枚举中排除该属性会将该属性从流程中完全排除,但是有没有办法进行更细粒度的控制?
Swift 4的新的Encodable
/Decodable
协议使JSON(反)序列化变得非常令人愉快。但是,我还没有找到一种方法可以对应该编码的属性和应该解码的属性进行细粒度控制。
我注意到,从随附的CodingKeys
枚举中排除该属性会将该属性从流程中完全排除,但是有没有办法进行更细粒度的控制?
Codable
协议(init(from:)
和encode(to:)
手动对过程的完全控制)。
Answers:
编码/解码的密钥列表由一种称为CodingKeys
(请注意s
末尾)的类型控制。编译器可以为您综合此内容,但始终可以覆盖该内容。
假设您要nickname
同时在编码和解码中排除该属性:
struct Person: Codable {
var firstName: String
var lastName: String
var nickname: String?
private enum CodingKeys: String, CodingKey {
case firstName, lastName
}
}
如果您希望它不对称(即编码而不是解码,反之亦然),则必须提供自己的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)
}
}
nickname
一个默认值。否则,将无法分配任何值给属性init(from:)
。
encode
在非对称示例中提供吗?由于这仍然是标准行为,因此我认为这不是必需的。只是decode
因为那是不对称的来源。
fullName
无法映射到存储的属性,因此必须提供自定义的编码器和解码器。
从编码器中排除某些属性的另一种方法,可以使用单独的编码容器
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"}
}
同样的方法可以用于解码器
尽管可以做到这一点,但最终最终会变得非常不灵活,甚至变得不JSONy。我想我知道您来自哪里,#id
s的概念在HTML中很盛行,但很少传递到JSON
我认为是好东西(TM)的世界。
如果您使用递归哈希重组文件,则某些Codable
结构将能够JSON
很好地解析您的文件,即,如果您recipe
仅包含一个数组,ingredients
而该数组又包含(一个或多个)ingredient_info
。这样,解析器将首先帮助您将网络拼接在一起,并且如果确实需要它们,则只需通过简单的遍历结构即可提供一些反向链接。由于这需要对您JSON
和您的数据结构进行彻底的修改,因此,我仅勾勒出一个想法供您考虑。如果您认为可以接受,请在评论中告诉我,然后我可以进一步详细说明,但是根据情况,您可能无权更改其中任何一个。
我已经使用了协议及其扩展名以及AssociatedObject来设置和获取图像(或需要从Codable中排除的任何属性)属性。
这样,我们就不必实现自己的编码器和解码器
这是代码,为了简单起见,保留了相关代码:
protocol SCAttachmentModelProtocol{
var image:UIImage? {get set}
var anotherProperty:Int {get set}
}
extension SCAttachmentModelProtocol where Self: SCAttachmentUploadRequestModel{
var image:UIImage? {
set{
//Use associated object property to set it
}
get{
//Use associated object property to get it
}
}
}
class SCAttachmentUploadRequestModel : SCAttachmentModelProtocol, Codable{
var anotherProperty:Int
}
现在,只要我们想访问Image属性,就可以在对象上使用协议确认(SCAttachmentModelProtocol)
CodingKeys
枚举中就足够了。