美文网首页
Swift Codable的一个拓展--XPath语法

Swift Codable的一个拓展--XPath语法

作者: CGPointZero | 来源:发表于2021-06-16 11:58 被阅读0次

    Codable JSON转模型,简单好用又高效,XPath快速JSON子节点定位,二者合二为一,简单高效快捷又好用。
    样例模型,Order与Goods都是Codable:

    struct Order: Codable {
        var id: String?
        var money: String?
        var goods: [Goods]?
    }
    
    struct Goods: Codable {
        var id: String?
        var name: String?
        var price: String?
        var count: Int?
    }
    

    JSON样例:

    {
        "msg": "",
        "data": {
            "orders": [{
                "id": "1234",
                "money": "65.0",
                "goods": [{
                    "id": "a10",
                    "price": "25,0",
                    "name": "重窝小旋风",
                    "count": 1
                }, {
                    "id": "b11",
                    "price": "20.0",
                    "name": "逍遥青",
                    "count": 2
                }]
            }, {
                "id": "5678",
                "money": "708.0",
                "goods": [{
                    "id": "c123",
                    "price": "708.0",
                    "name": "一帆精工擎天柱红色纪念款钓台",
                    "count": 1
                }]
            }],
            "total": 2
        },
        "code": 0
    }
    

    JSON转模型方法:

    public func decode<T : Decodable>(_ json : Any) -> T? {
        guard JSONSerialization.isValidJSONObject(json) else {
            return nil
        }
        guard let data = try? JSONSerialization.data(withJSONObject: json, options: .init()) else {
            return nil
        }
        let decoder = JSONDecoder()
        guard let result = try? decoder.decode(T.self, from: data) else {
            return nil
        }
        return result
    }
    
    public func decode<T : Decodable>(_ json : [String: Any], xpath: String) -> T? {
        guard let target = json.xpath(xpath) else {
            return nil
        }
        return decode(target)
    }
    

    Codable使用:

    let data = json["data"] as? [String: Any]
    let orders = data?["orders"] as? [[String: Any]] ?? []
    let results: [Order] = decode(orders)
    

    XPath+Codable,一行搞定:

    let results: [Order] = decode(json, xpath: "data.orders")
    

    结合RxSwift使用样例:

    func goodsList(page: Int, keywords: String? = nil, success: (([Goods]) -> ())?, failure: ErrorHandler?) {
            provider.rx.request(.goodsList(page: page, keywords: keywords))
                .mapCode()
                .mapObject([Goods].self, xpath: "data.spus")
                .subscribe { goods in
                    success?(goods)
                } onError: { error in
                    failure?(error)
                }
                .disposed(by: bag)
        }
    

    RxSwift之mapObject:

    public extension PrimitiveSequence where Trait == SingleTrait, Element == [String: Any] {
        func mapObject<T: Codable>(_ type: T.Type) -> Single<T> {
            return flatMap { dict in
                guard let t: T = decode(dict) else {
                    throw YXNetworkError.JsonMap
                }
                return .just(t)
            }
        }
        
        func mapObject<T: Codable>(_ type: T.Type, xpath: String) -> Single<T> {
            return flatMap { dict in
                guard let result = dict.xpath(xpath) else {
                    throw YXNetworkError.JsonMap
                }
                guard let t: T = decode(result) else {
                    throw YXNetworkError.JsonMap
                }
                return .just(t)
            }
        }
    }
    

    最后附上XPath的实现方法:

    public extension Dictionary where Key == String, Value == Any {
        func xpath(_ xpath: String) -> Any? {
            guard xpath.count > 0 else {
                return nil
            }
            let paths = xpath.components(separatedBy: ".")
            var result = self
            var correct = true
            for i in 0 ..< paths.count {
                let path = paths[i]
                if i < paths.count - 1 {
                    if let next = result[path] as? [String: Any] {
                        result = next
                    } else {
                        correct = false
                        break
                    }
                } else {
                    if let next = result[path] {
                        return next
                    } else {
                        correct = false
                        break
                    }
                }
            }
            guard correct else {
                return nil
            }
            return result
        }
    }
    

    相关文章

      网友评论

          本文标题:Swift Codable的一个拓展--XPath语法

          本文链接:https://www.haomeiwen.com/subject/efmxyltx.html