前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Swift Codable 将任意类型解析为想要的类型

Swift Codable 将任意类型解析为想要的类型

作者头像
韦弦zhy
发布2021-11-24 14:06:27
1.8K0
发布2021-11-24 14:06:27
举报

默认情况下,使用 Swift 内置的 Codable API 解析 JSON 时,我们的属性类型需要和Json 中的类型保持一致,否则就会解析失败。

例如我们有如下JSON:

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

则我们常用的模型如下:

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

这个时候我们正常解析则没有任何问题,但是当出现服务器将 age中的18采用String方式:"18" 返回时,则无法解析,这是非常难遇见的情况(请问为啥我遇到了???)。另一种常见的是返回了"18.1", 这是一个 Double类型,这时候一样无法成功解析。在使用 OC 的时候,我们常用的方法将其解析为 NSString 类型,使用的时候再进行转换,可是当使用 Swift 的 Codabel 时我们不能直接做到这样。

1、如果服务器只会以 String 方式返回 Age 同时能确认里面是 Int 还是 Double

这是一种最常见的情况可以采用 Codable 自定义解析 JSON 中提到的值转换来完成:

代码语言:javascript
复制
protocol StringRepresentable: CustomStringConvertible {
    init?(_ string: String)
}

extension Int: StringRepresentable {}

struct StringBacked<Value: StringRepresentable>: Codable {
    var value: Value
    
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        let string = try container.decode(String.self)
        
        guard let value = Value(string) else {
            throw DecodingError.dataCorruptedError(
                in: container,
                debugDescription: "Failed to convert an instance of \(Value.self) from '\(string)'"
            )
        }
        
        self.value = value
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(value.description)
    }
}

这个时候,我们的模型如下:

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

struct User: Codable {
    var name: String
    var ageInt: Int {
        get { return age.value }
        set { age.value = newValue}
    }
    private var age: StringBacked<Int>
}
2、如果遇到了上面提到的其他情况呢?

第一种处理方法会改变原有数据结构,虽然对于直接重写 User 的解析过程来说,拥有更多的通用性,但是遇到其他情况则束手无策。

第二种方法同时也不会采用重写模型自身的解析过程来实现,那样子不具备通用性,太麻烦,每次遇到都需要来一遍。

参照第一种方法,我们先写一个将任意类型转换成 String? 的方法:

代码语言:javascript
复制
//  用于解决不知道服务器返回什么类型。。。。都转换为 String 然后保证正常解析
//  当前支持 Double Int String
//  其他类型会解析成 nil
//
/// 将 String Int Double 解析为 String? 的包装器
@propertyWrapper public struct ZYString: Codable {
    public var wrappedValue: String?
    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        var string: String?
        do {
            string = try container.decode(String.self)
        } catch {
            do {
                try string = String(try container.decode(Int.self))
            } catch {
                do {
                    try string = String(try container.decode(Double.self))
                } catch {
                    // 如果不想要 String? 可以在此处给 string 赋值  = “”
                    string = nil
                }
            }
        }
        wrappedValue = string
    }
}

这里面可以无限套娃,比如如果是这个字段返回的是字典,你可以将字典解析出来处理成字符串~~~

此时 User 写成:

代码语言:javascript
复制
struct User: Codable {
    var name: String
    @ZYString public var age: String?
}

同理我们可以写一个 ZYInt, 来将任意类型转换为 Int 如果确实无法转换,我们可以控制其为nil 或者直接等于 0,这样我们就可以保证不管怎么样,我们的解析不会失败。

此时 User 写成:

代码语言:javascript
复制
struct User: Codable {
    var name: String
    @ZYInt public var age: Int
}

看起来这个地方影响很小,只有User解析失败没什么,当遇到整个页面都是用一个Json返回时,不管是哪个局部出现问题,都会导致真个页面解析失败,所以还是要做好兼容操作。。。特别是后台不太稳定的情况~

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、如果服务器只会以 String 方式返回 Age 同时能确认里面是 Int 还是 Double
  • 2、如果遇到了上面提到的其他情况呢?
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档