Codable实现json转Model,是时候干掉HandyJSON了!

自从开始使用Swift作项目,一直都在使用HandyJSON,不能否认,HandyJSON在Swift4.0之前是个好东西,也尝试过其它json转mode的工具,最终发现仍是HandyJSON最好用. 去年Swift4.0发布以后,一个最有趣的变化就是Codable协议. 一直都知道Codable来实现json转model,不但效率高,而且简单易用, 可是一直拖到最近才简单封装个小工具,为何呢? 😂!!!json

在工具的封装上,参考了HandyJSON的部分代码,在使用HandyJSON过程当中,一直都以为designatedPath是一个很牛逼的存在,开发效率提高了不是一个量级,因而这里也参考HandyJSON中designatedPath的实现代码,并根据Codable的须要改形成fileprivate func getInnerObject(inside jsonData: Data?, by designatedPath: String?) -> Data?方法. 在这里感谢HandyJSON开发组.swift

Codable扩展所有代码以下所示.100余行的代码先后花费一天多的时间. 当了解到google工程师日均一百多行的代码量,我以为这速度还能够吧. 在代码质量和阅读质量上不敢说有多好,我只是按照本身认为最好的方式来作,若是有哪里不当或者能够用更好方法解决的地方,烦请各位告知,彼此交流学习.数组

Codable协议扩展实现代码以下所示:ide

//
//  CodableHelper.swift
//  CodableDemo
//
//  Created by Walden on 2018/5/7.
//  Copyright © 2018年 Walden. All rights reserved.
//

import Foundation

//扩展Encodable协议,添加编码的方法
public extension Encodable {
    //1.遵照Codable协议的对象转json字符串
    public func toJSONString() -> String? {
        guard let data = try? JSONEncoder().encode(self) else {
            return nil
        }
        return String(data: data, encoding: .utf8)
    }
    
    //2.对象转换成jsonObject
    public func toJSONObject() -> Any? {
        guard let data = try? JSONEncoder().encode(self) else {
            return nil
        }
        return try? JSONSerialization.jsonObject(with: data, options: .allowFragments)
    }
}

//扩展Decodable协议,添加解码的方法
public extension Decodable {
    //3.json字符串转对象&数组
    public static func decodeJSON(from string: String?, designatedPath: String? = nil) -> Self? {
        
        guard let data = string?.data(using: .utf8),
            let jsonData = getInnerObject(inside: data, by: designatedPath) else {
                return nil
        }
        return try? JSONDecoder().decode(Self.self, from: jsonData)
    }
    
    //4.jsonObject转换对象或者数组
    public static func decodeJSON(from jsonObject: Any?, designatedPath: String? = nil) -> Self? {
        
        guard let jsonObject = jsonObject,
            JSONSerialization.isValidJSONObject(jsonObject),
            let data = try? JSONSerialization.data(withJSONObject: jsonObject, options: []),
            let jsonData = getInnerObject(inside: data, by: designatedPath)  else {
                return nil
        }
        return try? JSONDecoder().decode(Self.self, from: jsonData)
    }
}

//扩展Array,添加将jsonString或者jsonObject解码到对应对象数组的方法
public extension Array where Element: Codable {
    
    public static func decodeJSON(from jsonString: String?, designatedPath: String? = nil) -> [Element?]? {
        guard let data = jsonString?.data(using: .utf8),
            let jsonData = getInnerObject(inside: data, by: designatedPath),
            let jsonObject = try? JSONSerialization.jsonObject(with: jsonData, options: .allowFragments) as? [Any] else {
            return nil
        }
        return Array.decodeJSON(from: jsonObject)
    }
    
    public static func decodeJSON(from array: [Any]?) -> [Element?]? {
        return array?.map({ (item) -> Element? in
            return Element.decodeJSON(from: item)
        })
    }
}


/// 借鉴HandyJSON中方法,根据designatedPath获取object中数据
///
/// - Parameters:
///   - jsonData: json data
///   - designatedPath: 获取json object中指定路径
/// - Returns: 多是json object
fileprivate func getInnerObject(inside jsonData: Data?, by designatedPath: String?) -> Data? {

    //保证jsonData不为空,designatedPath有效
    guard let _jsonData = jsonData,
        let paths = designatedPath?.components(separatedBy: "."),
        paths.count > 0 else {
        return jsonData
    }
    //从jsonObject中取出designatedPath指定的jsonObject
    let jsonObject = try? JSONSerialization.jsonObject(with: _jsonData, options: .allowFragments)
    var result: Any? = jsonObject
    var abort = false
    var next = jsonObject as? [String: Any]
    paths.forEach({ (seg) in
        if seg.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) == "" || abort {
            return
        }
        if let _next = next?[seg] {
            result = _next
            next = _next as? [String: Any]
        } else {
            abort = true
        }
    })
    //判断条件保证返回正确结果,保证没有流产,保证jsonObject转换成了Data类型
    guard abort == false,
        let resultJsonObject = result,
        let data = try? JSONSerialization.data(withJSONObject: resultJsonObject, options: []) else {
        return nil
    }
    return data
}

CodableHelper工具的使用也是很是简单的,代码以下所示:工具

//首先定义一个结构体Person用来表示数据Model
struct Person: Codable {
    var name: String?
    var age: Int?
    var sex: String?
}


//1.jsonString中获取数据封装成Model
let p1String = "{\"name\":\"walden\",\"age\":30,\"sex\":\"man\"}"
let p1 = Person.decodeJSON(from: p1String)

//2.jsonString中获取数据封装成Array
let personString = "{\"haha\":[{\"name\":\"walden\",\"age\":30,\"sex\":\"man\"},{\"name\":\"healer\",\"age\":20,\"sex\":\"female\"}]}"
let persons = [Person].decodeJSON(from: personString, designatedPath: "haha")

//3.对象转jsonString
let jsonString = p1?.toJSONString()

//4.对象转jsonObject
let jsonObject = p1?.toJSONObject()

目前存在问题:学习

  1. Model中定义的数据类型和jsonString中数据类型不对应时候会致使解析失败;
  • 转载请注明出处
相关文章
相关标签/搜索