前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Swift Codable 记录解析路径

Swift Codable 记录解析路径

作者头像
韦弦zhy
发布2021-11-24 14:07:18
8050
发布2021-11-24 14:07:18
举报
文章被收录于专栏:韦弦的偶尔分享

在我们的工作中,各种特殊情况都有可能遇到,某些特定情况下,需要我们记录模型的解析路径,例如:

代码语言:javascript
复制
{
    "owner":{
        "name":"zhy",
        "age":18
    },
    "visitor":{
        "name":"weixian",
        "age":18
    }
}

对应的模型如下:

代码语言:javascript
复制
struct Role: Codable {
    var owner: User
    var visitor: User
}

struct User: Codable {
    var name: String
    var age: Int
}
1、我们想要知道 Role的属性的名字的字符串,这个时候利用 Codable 中的Decoder协议提供的 API 来获取解析路径:
代码语言:javascript
复制
/// A type that can decode values from a native format into in-memory
/// representations.
public protocol Decoder {

    /// The path of coding keys taken to get to this point in decoding.
    var codingPath: [CodingKey] { get }
    ...
}

codingPath 的注释为: 在解码中达到这一点所采用的编码 Key 路径。

所以我们重写User的解析过程,并新增属性存储解析路径:

代码语言:javascript
复制
struct User: Codable {
    var name: String
    var age: Int
    var path: String
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.name = try container.decode(String.self, forKey: .name)
        self.age = try container.decode(Int.self, forKey: .age)
        self.path = decoder.codingPath.last?.stringValue ?? ""
    }
}

此时,我们拿到解析结果 owner.path 即为 owner

2. 我们想要知道 name 的属性名字的字符串,同时获取对应角色名 即想知道其是 owner.name 还是 visitor.name

我们仍准备采用上面的方法,但是我没找到直接重写 Int, String 解析过程的方法,就算是找到了,好像也太麻烦了,同时还需要手动添加 path属性, 有简单的方法吗?

有,利用属性包装器 可以帮我们简化调用,利用 OC runtime 的属性关联值以及Swift 的协议默认实现可以帮我们新增属性:

我们扩展DeCodable 协议,要求所有遵守此协议的对象持有 codablePath 属性和 codablePathName 属性,并且提供默认实现:

代码语言:javascript
复制
struct ZYAssociateKeys {
    static var ZYCodablePath: String = "ZYCodablePath"
    static var ZYCodablePathName: String = "ZYCodablePathName"
    
}

public extension Decodable {
    var codablePath: String {
        get { return objc_getAssociatedObject(self, &ZYAssociateKeys.ZYCodablePath) as? String  ?? ""}
        set { objc_setAssociatedObject(self, &ZYAssociateKeys.ZYCodablePath, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC) }
    }
    
    var codablePathName: String {
        get { return objc_getAssociatedObject(self, &ZYAssociateKeys.ZYCodablePathName) as? String  ?? ""}
        set { objc_setAssociatedObject(self, &ZYAssociateKeys.ZYCodablePathName, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC) }
    }
}

此时不管是 Role 还是 User 亦或者是 String, Int, Double 之类都将自动获得这两个属性

下面我们实现一个 Path 属性包装器:

代码语言:javascript
复制
@propertyWrapper struct Path<Value: Codable>: Codable {
    var wrappedValue: Value
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        wrappedValue = try container.decode(Value.self)

        decoder.codingPath.forEach {
            if !wrappedValue.codablePath.isEmpty {
                wrappedValue.codablePath.append(".")
            }
            wrappedValue.codablePath.append($0.stringValue)
        }
        wrappedValue.codablePathName = decoder.codingPath.last?.stringValue ?? ""
    }
}

typealias path = Path

此时我们的模型写成:

代码语言:javascript
复制
struct Role: Codable {
    @path var owner: User
    @path var visitor: User
}

struct User: Codable {
    @path var name: String
    @path var age: Int
}

我们获取一下结果看是否符合预期:

代码语言:javascript
复制
let model = try? JSONDecoder().decode(Role.self, from: json.data(using: .utf8)!)

let oP = model?.owner.codablePath
let opN = model?.owner.codablePathName
let namePath = model?.owner.name.codablePath
let namePahtName = model?.owner.name.codablePathName

print(oP)
print(opN)
print(namePath)
print(namePahtName)

/* ---- log ---- */
Optional("")
Optional("")
Optional("owner.name")
Optional("name")

很遗憾,并没有达到我们的预期,这是因为swift 中 struct 是值类型,无法利用关联值给其新增属性,所以只能改成:

代码语言:javascript
复制
class User: Codable {
    @path var name: String
    @path var age: Int
}

这个是时候变一切都按预期运行了~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/9/30 上,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、我们想要知道 Role的属性的名字的字符串,这个时候利用 Codable 中的Decoder协议提供的 API 来获取解析路径:
  • 2. 我们想要知道 name 的属性名字的字符串,同时获取对应角色名 即想知道其是 owner.name 还是 visitor.name
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档