Swift08-Mirror 的应用Json解析&错误处理

334 阅读5分钟

Swift 进阶之路 文章汇总

前言:

上篇文章分析了Mirror的特性,以及从源码的角度查看了Mirror的构成,根据Mirror的这个特性思考一个问题,长篇大论的Mirror能干什么?那就引出了本文的内容Json解析Error处理

Json解析

基础的Json解析

定义了一个LGTeacher类,然后通过一个test方法来解析t

分析:

  • mirror成功读取对象属性名属性值,可使用字典存储和输出

  • 每个属性都会默认当作对象来处理,通过mirror.children可判断当前对象是否拥有属性,没有就跳出当前属性递归流程。 这样可保证所有读取children完整层级信息

  • 递归实际是利用特性,直接间接 调用自身实现层级读取需求,递归需要有明确的终止条件

JSON解析封装

上面只针对一个类的Json解析,如果需要针对通用个类,就要对Json解析进行封装,提取JSON解析协议,然后提供一个默认实现,让类遵守协议

//1、定义一个JSON解析协议
protocol CustomJSONMap {
    func jsonMap() -> Any
}
//2、默认实现
extension CustomJSONMap{
    func jsonMap() -> Any{
        let mirror = Mirror(reflecting: self)
        //递归终止条件
        guard !mirror.children.isEmpty else {
            return self
        }
        //字典,用于存储json数据
        var keyValue: [String: Any] = [:]
        //遍历
        for children in mirror.children {
            if let value = children.value as? CustomJSONMap {
                if let keyName = children.label {
                    //递归
                    keyValue[keyName] = value.jsonMap()
                }else{
                    print("key是nil")
                }
            }else{
                print("当前-\(children.value)异常")
            }
        }
        return keyValue
    }
}


//3、让类遵守协议(注意:类中属性的类型也需要遵守协议,否则无法解析)
class LGTeacher: CustomJSONMap {
    var age = 18
    var name = "LGCat"
}


//使用
var t = LGTeacher()
print(t.jsonMap())

打印异常:

分析,可看到if let value = children.value as? CustomJSONMapfalse,其子项目没有遵守CustomJSONMap协议,顾打印不出来

调整代码,使子节点也遵守CustomJSONMap协议

Json的封装代码,可看出可以自动的解析对象,但在发生错误的情况下我发达到预期的效果,那么Mirror的方案如何处理错误信息呢?请看下方的分析

错误处理

Swift中提供了Error协议来标识当前应⽤程序发⽣错误的情况,定义如下:

public protocol Error {
}

Error是一个空协议,其中没有任何实现,这也就意味着你可以遵守该协议,然后自定义错误类型。所以不管是我们的struct、Class、enum,我们都可以遵循这个Error来表示一个错误.

所以接下来,采用enum方式对上面封装的JSON解析修改其中的错误处理.

自定义一个枚举类型错误枚举,让其遵守Error协议,将默认实现的print替换成枚举类型

//定义错误类型
enum JSONMapError: Error{
    case emptyKey
    case notConformProtocol
}

//1、定义一个JSON解析协议
protocol CustomJSONMap {
    func jsonMap() -> Any
}
//2、提供一个默认实现
extension CustomJSONMap{
    func jsonMap() -> Any{
        let mirror = Mirror(reflecting: self)
        //递归终止条件
        guard !mirror.children.isEmpty else {
            return self
        }
        //字典,用于存储json数据
        var keyValue: [String: Any] = [:]
        //遍历
        for children in mirror.children {
            if let value = children.value as? CustomJSONMap {
                if let keyName = children.label {
                    //递归
                    keyValue[keyName] = value.jsonMap()
                }else{
                    return JSONMapError.emptyKey
                }
            }else{
                return JSONMapError.notConformProtocol
            }
        }
        return keyValue
    }
}

由上面的代码中的jsonMap方法,返回值是Any类型,但是JSONMapError协议无法确认返回值是什么,那么该如何区分呢?即如何抛出错误呢?在这里可以通过throw关键字(即将Demo中原本return的错误,改成throw抛出),jsonMap方法中递归调用,所以还需要在递归调用前增加 try 关键字,最终完整的错误表达方式如下:

extension CustomJSONMap{
    func jsonMap() throws -> Any{
        let mirror = Mirror(reflecting: self)
        //递归终止条件
        guard !mirror.children.isEmpty else {
            return self
        }
        //字典,用于存储json数据
        var keyValue: [String: Any] = [:]
        //遍历
        for children in mirror.children {
            if let value = children.value as? CustomJSONMap {
                if let keyName = children.label {
                    //递归
                    keyValue[keyName] = try value.jsonMap()
                }else{
                    throw JSONMapError.emptyKey
                }
            }else{
                throw JSONMapError.notConformProtocol
            }
        }
        return keyValue
    }
}
var t = LGTeacher()
print(try t.jsonMap())

参考资料:《Swift值的关键字throw和rethrows

swift中错误处理的方式

方式一 :try

使用try关键字,是最简便的,即甩锅,将这个抛出给别人(向上抛出,抛给上层函数),在使用时,需要注意以下两点:

  • try? 返回一个可选类型,只有两种结果:

       A: 要么成功,返回具体字典值

       B:要么错误,但并不关心是哪种错误,统一返回nil

  • try!使用你绝对自信的认为这行代码是对的,这行代码绝对不会发生错误.

方式二:do...catch

可在《Swift值的关键字throw和rethrows》中获得答案

LocalizedError协议

如果只是用Error还不足以表达更详尽的错误信息,可以使用LocalizedError协议,其定义如下:

此协议遵守Error协议,对JSONMapError枚举增加一个扩展,遵守LocalizedError协议,可以打印更详细的错误信息

//定义更详细的错误信息
extension JSONMapError: LocalizedError{
    var errorDescription: String?{
        switch self {
        case .emptyKey:
            return "key为空"
        case .notConformProtocol:
            return "没有遵守协议"
        }
    }
}

CustomNSError协议

CustomNSError相当于OC中的NSError

具体的代码实现:

//CustomNSError相当于NSError
extension JSONMapError: CustomNSError{
    var errorCode: Int{
        switch self {
        case .emptyKey:
            return 404
        case .notConformProtocol:
            return 504
        }
    }
}

总结

  • Swift中处理错误信息的基础协议是Error协议,其中LocalizedErrorCustomNSError都遵守了Error协议

  • 处理错误信息的方法有do...catchtry方法

  • 需要同时由返回正常值错误信息,需要通过throw关键字配合使用,在方法的返回值箭头前必须要加入throws关键字,否则throw关键字会报错

  • 如果没有很自信的确认代码没有问题,请不要使用try!方式处理异常信息

  • 在使用try?的过程中只有两种结果要么是nil,要么是成功