swift 5.1 Json转换之Codable

          对于开发的人员来说,接受后台的数据,并转换成自己的数据模型是常见的事情。但是作为苹果开发者,并没有一个很好的工具去直接转换,必须借助与第三方的开发的库。那么比较好用的有YYModel、MJExtension。当然还有其他的库。但是有个问题,对于第三方库,如果使用过多对于我们开发者来说并不是一件好事。简单分析一下:优点:功能强大,使用方便,提高开发效率,相对较安全出现bug几率较小。缺点:受限于第三方(主要是版本),如果有bug不容易修改,甚至只能官方修改,增大开发包,使用过多容易导致编译速度极慢,实际用到的功能很少。基于以上选择,使不使用第三方自己斟酌,我们这篇文章,在这不做讨论,对于swift而言,我们这篇文章主要讲怎么使用官方的Codable转换成自己的模型。下面进入正题。

swift引入了Codable编码与解码的功能,简单来说就是支持把一个json,转成自己创建并遵从Codable协议的一个数据模型,可以是类,也可以是结构体struct,对于结构体这个是第三方所没有的一个功能。苹果官方推荐使用结构体,方便,安全,占用内存少,还不用担心内存释放问题,好处多多。我们针对struct来说一下。

首先我们先创建一个json数据:

{
    "name": "张三",
    "age": 20,
    "birthday": "1990-06",
    "location": "中国",
    "job": "教师"
}

然后我们创建一个结构体模型接收这个数据:

struct UserModel: Codable {
    var name = "";
    var age = 0;
    var birthday = "";
    var location = "";
    var job = "";
}

说明这个结构体模型必须实现Codable协议。

然后我们去进行decode解码:

let dict = """
{
    "name": "张三",
    "age": 20,
    "birthday": "1990-06",
    "location": "中国",
    "job": "教师"
}
"""
let decode = JSONDecoder();
if let data = dict.data(using: .utf8) {
    let item = try? decode.decode(UserModel.self, from: data);
    print("item: \(item)");
}

同过打印我们发现,可以正常输出struct的各个值,使用起来就是这么简单。

2.对于复杂的json我们也是可以解析的。比如下面的json:

{
        "name": "张三",
        "age": 20,
        "birthday": "1990-06",
        "location": "中国",
        "job": "教师",
        "students": [
            {
                "name": "小丽",
                "age": 12,
                "id": 1234,
                "sex": "女"
            },
            {
                "name": "小万",
                "age": 11,
                "id": 1235,
                "sex": "男"
            },
            {
                "name": "小明",
                "age": 14,
                "id": 1237,
                "sex": "男"
            }
        ]
    }

上边这个是比较复杂的,一个对象包括了数组,数组里面又有对象,那么对于这个struct模型我们可以这样创建:

struct UserModel: Codable {
    var name = "";
    var age = 0;
    var birthday = "";
    var location = "";
    var job = "";
    var students: [StudentsModel]!
}

struct StudentsModel: Codable {
    var name = "";
    var age = 0;
    var id = 0;
    var sex = "";
}

使用起来还是上边的那个方法我们可以看到students打印出来的是个数组。

但是有时候,情况并不我们想象中的那么完美,尤其是在命名规则上,后台有可能会出现带下滑杠的命名方式比如:student_name等形式,而swift一般是驼峰是命名比如:studentName,这就带来了一个问题,这两个显示是不匹配的,那么我们怎么解决这个问题呢,比如我们有如下的json数据:

{
    "name": "张三",
    "phone_number": 18467898765,
    "pic_url": "中国"
}

这个时候我的需要把手动把属性和值对应起来。

struct UserModel: Codable {
    var name = "";
    var phoneNumber = 0;
    var picURL = "";
    
    enum UserKeys: String, CodingKey {
        case phoneNumber = "phone_number"
        case picURL = "pic_url"
        case name = "name"
    }
    
    init(from decoder: Decoder) throws{
        let value = try? decoder.container(keyedBy: UserKeys.self);
        
        phoneNumber = (try? value?.decode(Int.self, forKey: .phoneNumber)) ?? 0;
        picURL = (try? value?.decode(String.self, forKey: .picURL)) ?? "";
        name = (try? value?.decode(String.self, forKey: .name)) ?? "";
        
    }
}

从上面也可以看出,当键和值的类型不一样的时候我们也是可以转换的。

swift的Codable很简单也很强大,如果我们在实际开发的时候,后台返回来的数据并不是完整的,如果没有那个值可能就不返回来了,这个时候我们上面的问题就暴露出来了。具体是什么错误呢。我们把数据json的name去掉,并改成如下的形式。

数据:

{
    "age": 12,
    "birthday": "2000-01"
}

模型:

struct UserModel: Codable {
    var name = "";
    var age = 0;
    var birthday = ""; 
}

测试方法我们也变一下:

let decode = JSONDecoder();
    if let data = stringValue.data(using: .utf8) {
        do {
            let item = try decode.decode(UserModel.self, from: data);
            print("item: \(item)");
        } catch  {
            print("error =\(error)");
        }
        
    }

会打印如下的错误信息:

error =keyNotFound(CodingKeys(stringValue: "name", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"name\", intValue: nil) (\"name\").", underlyingError: nil))

就是我们定义的name是必须有的,但是后台没有返回所以就出错了,这个时候我们要定义成可选类型,如下:

struct UserModel: Codable {
    var name: String?
    var age: Int?
    var birthday: String?
}

这个时候就可以放心的使用了,发现成功答应值,只是name是空。

其实我们发现这些属性的值我们使用过程中一般都是读取,为了更好的表达这个意图,我们一般定义成下面这样,保证了数据的安全:

struct UserModel: Codable {
    let name: String?
    let age: Int?
    let birthday: String?
}

为了更好的使用这个Codable这个特性,我们可以封装一下,给JSONDecoder添加一个扩展,具题如下:

 

extension JSONDecoder {
    class func jsonDecoder(_ type: T.Type,from data: Data?) -> T? {
        guard let data = data else {
            return nil;
        }
        let json = JSONDecoder();
        var jsonItem: T? = nil;
        do {
            jsonItem = try json.decode(type, from: data)
        } catch {
            printObject("data convert json error: \(error)");
        }
        
        return jsonItem;
    }
    class func jsonDecoder(_ type: T.Type,fromAny data: Any?) -> T? {
        if let data = data as? Data {
            return jsonDecoder(type, from: data);
        }
        guard let data = data else {
            return nil;
        }
        if let stringValue = data as? String {
            return jsonDecoder(type, from: stringValue.data(using: .utf8));
        }
        let jsonData = try? JSONSerialization.data(withJSONObject: data, options: .prettyPrinted);
        return jsonDecoder(type, from: jsonData);
    }
}

这样使用起来就方便了许多。好了对于Codable的使用我就说这么多,还有编码也是一样的,可以自己探索一下。

还是那句老话:学习的路上,总不缺少努力的人。祝你越来越。

你可能感兴趣的:(swift,Codable,json)