swift Codableの使用と簡単なパッケージ


swiftは4.0までデータ解析の方法がなかった.現在4.0以降,Codableが直接jsonをオブジェクトに変換できるようになり,OCのKVCメカニズムに取って代わることが期待される.まずコダックを見てみましょう
public typealias Codable = Decodable & Encodable 

きほんしよう
NSCodingに似ていて、decoderとencoderを書き換えることができます.基本的な使い方は簡単です
class Person: Codable {
      var name : String = ""
      var age : Int = 0
}

//json   
let JSONString = "{"name":"xiaoming","age":10}"
guard let jsonData = JSONString.data(using: .utf8) else {
        return
}
let decoder = JSONDecoder()
guard let obj = try? decoder.decode(Person.self, from: jsonData) else {
        return
}
print(obj.name) //xiaoming
print(obj.age)  //10

CodingKey
CodableといえばCodingKeyが欠かせませんが、上記の例はjsonデータがmodel属性に1つずつ対応している場合にのみ適用され、対応するkeyも一致しなければなりませんが、CodingKeyは開発中によく遭遇するバックグラウンドから与えられるkeyがappと異なる場合を解決することができます
class Person: Codable {
      var name : String = ""
      var age : Int = 0

      enum CodingKeys : String, CodingKey {
            case name = "name_a"
            case age
        }
}

//json   
let JSONString = "{"name_a":"xiaoming","age":10}"
guard let jsonData = JSONString.data(using: .utf8) else {
        return
}
let decoder = JSONDecoder()
guard let obj = try? decoder.decode(Person.self, from: jsonData) else {
        return
}
print(obj.name) //xiaoming

また,一部のデータを受信する必要がない場合には,CodingKeyも扱うことができ,主にCodingKeysの列挙にこのkeyを書かないで済む.
class Person: Codable {
      var name : String = ""
      var age : Int = 0
      var score : Double = 0.00

      enum CodingKeys : String, CodingKey {
            case name = "name_a"
            case age
        }
}

//json   
let JSONString = "{"name_a":"xiaoming","age":10,"score":98.5}"
guard let jsonData = JSONString.data(using: .utf8) else {
        return
}
let decoder = JSONDecoder()
guard let obj = try? decoder.decode(Person.self, from: jsonData) else {
        return
}
print(obj.score) //0.00

パッヶージ
Codableのメカニズムに基づいて、プロジェクトに簡単にカプセル化することができます.現在よく見られるネットワークフレームワークでは、jsonデータを直接辞書に変換するため、辞書をjsonとjsonに変換する方法が追加されています.
//
//  Custom.swift
//      
//
//  Created by qiuchengxiang@gmail.com on 2018/1/3.
//  Copyright © 2018  Zillion Fortune. All rights reserved.
//

import Foundation

fileprivate enum MapError: Error {
    case jsonToModelFail    //json model  
    case jsonToDataFail     //json data  
    case dictToJsonFail     //   json  
    case jsonToArrFail      //json     
    case modelToJsonFail    //model json  
}

protocol Mappable: Codable {
    func modelMapFinished()
    mutating func structMapFinished()
}

extension Mappable {

    func modelMapFinished() {}

    mutating func structMapFinished() {}

    //     
    func reflectToDict() -> [String:Any] {
        let mirro = Mirror(reflecting: self)
        var dict = [String:Any]()
        for case let (key?, value) in mirro.children {
            dict[key] = value
        }
        return dict
    }


    //     
    static func mapFromDict(_ dict : [String:Any], _ type:T.Type) throws -> T {
        guard let JSONString = dict.toJSONString() else {
            print(MapError.dictToJsonFail)
            throw MapError.dictToJsonFail
        }
        guard let jsonData = JSONString.data(using: .utf8) else {
            print(MapError.jsonToDataFail)
            throw MapError.jsonToDataFail
        }
        let decoder = JSONDecoder()

        if let obj = try? decoder.decode(type, from: jsonData) {
            var vobj = obj
            let mirro = Mirror(reflecting: vobj)
            if mirro.displayStyle == Mirror.DisplayStyle.struct {
                vobj.structMapFinished()
            }
            if mirro.displayStyle == Mirror.DisplayStyle.class {
                vobj.modelMapFinished()
            }
            return vobj
        }
        print(MapError.jsonToModelFail)
        throw MapError.jsonToModelFail
    }


    //JSON   
    static func mapFromJson(_ JSONString : String, _ type:T.Type) throws -> T {
        guard let jsonData = JSONString.data(using: .utf8) else {
            print(MapError.jsonToDataFail)
            throw MapError.jsonToDataFail
        }
        let decoder = JSONDecoder()
        if let obj = try? decoder.decode(type, from: jsonData) {
            return obj
        }
        print(MapError.jsonToModelFail)
        throw MapError.jsonToModelFail
    }


    //   json   
    func toJSONString() throws -> String {
        if let str = self.reflectToDict().toJSONString() {
            return str
        }
        print(MapError.modelToJsonFail)
        throw MapError.modelToJsonFail
    }
}


extension Array {

    func toJSONString() -> String? {
        if (!JSONSerialization.isValidJSONObject(self)) {
            print("dict json  ")
            return nil
        }
        if let newData : Data = try? JSONSerialization.data(withJSONObject: self, options: []) {
            let JSONString = NSString(data:newData as Data,encoding: String.Encoding.utf8.rawValue)
            return JSONString as String? ?? nil
        }
        print("dict json  ")
        return nil
    }

    func mapFromJson(_ type:[T].Type) throws -> Array {
        guard let JSONString = self.toJSONString() else {
            print(MapError.dictToJsonFail)
            throw MapError.dictToJsonFail
        }
        guard let jsonData = JSONString.data(using: .utf8) else {
            print(MapError.jsonToDataFail)
            throw MapError.jsonToDataFail
        }
        let decoder = JSONDecoder()
        if let obj = try? decoder.decode(type, from: jsonData) {
            return obj
        }
        print(MapError.jsonToArrFail)
        throw MapError.jsonToArrFail
    }
}


extension Dictionary {
    func toJSONString() -> String? {
        if (!JSONSerialization.isValidJSONObject(self)) {
            print("dict json  ")
            return nil
        }
        if let newData : Data = try? JSONSerialization.data(withJSONObject: self, options: []) {
            let JSONString = NSString(data:newData as Data,encoding: String.Encoding.utf8.rawValue)
            return JSONString as String? ?? nil
        }
        print("dict json  ")
        return nil
    }
}


extension String {
    func toDict() -> [String:Any]? {
        guard let jsonData:Data = self.data(using: .utf8) else {
            print("json dict  ")
            return nil
        }
        if let dict = try? JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) {
            return dict as? [String : Any] ?? ["":""]
        }
        print("json dict  ")
        return nil
    }
}

使用
enum BeerStyle : String, Codable {
    case ipa
    case stout
    case kolsch
    // ...
}



struct Beers: Mappable {

    var arrs : [Beer]
    var name : String

    mutating func structMapFinished() {
        name = "ngdgdfg"
    }

    struct Beer: Mappable {

        var name: String = "fsdf"
        var brewery: String = "fsdfsdf"
        var style: BeerStyle = .ipa
        var score: Double = 0.00
        var p: Person?



        enum CodingKeys : String, CodingKey {
            case name = "name_a"
            case brewery
            case p
            case score
        }


        class Person: Mappable {
            var name : String = ""
            var age : Int = 0
        }

    }
}


let dict1 = ["name_a":"nckjs","brewery":"gdfge","style":"stout","score":60.3,"p":["name":"jokh","age":10]] as [String : Any]
        let dict2 = ["name_a":"nckjs","brewery":"gdfge","style":"stout","score":60.3,"p":["name":"jokh","age":10]] as [String : Any]
        let dict3 = ["name_a":"nckjs","brewery":"gdfge","style":"stout","score":60.3,"p":["name":"jokh","age":10]] as [String : Any]

let arr = [dict1,dict2,dict3]
let dict4 = ["arrs" : arr, "name":"fgsdfs"] as [String : Any]
if let beers = try? Beers.mapFromDict(dict4, Beers.self) {
            print(beers)
}

ここでmap完了後のコールバック法を追加したが,Codableの解析は,再オブジェクト属性のset法ではなく,OCのKVC原理とは異なるためである.したがって、いくつかの付与が完了した後の操作では、このコールバックメソッドを使用する必要があります.また、classとstructのタイプを区別し、対応する方法を選択することに注意する必要があります.
nullの処理
バックグラウンドで値ごとにnullが返される可能性があるというメッセージがあり、属性をオプションにしたくないのでnullの処理を増やし、nullに遭遇したら再init(from decoder:Decoder)throwsの方法が必要になる
class Person: Codable {
            var name : String = ""
            var age : Int = 0

            init() {

            }

            required init(from decoder: Decoder) throws {
                let container = try decoder.container(keyedBy: CodingKeys.self)
                age = try container.decodeNil(forKey: .age) ? 10 : try container.decode(Int.self, forKey: .age)
                name = try container.decode(String.self, forKey: .name)
            }
        }

        let p = Person()
        let str = "{\"name\":\"fdfgdf\",\"age\":null}"
        let jsonData = str.data(using: .utf8)

        let decoder = JSONDecoder()

        if let obj = try? decoder.decode(Person.self, from: jsonData!) {
            print(obj.name,obj.age)
        }else {
            print("    ")
        }

decodeNil(forKey:KeyedDecodingContainer.Key)throws->Bool解析時に対応する属性がnull値であるか否かを判断し、trueを返すならnullでないならfalseを返す
以上のパッケージは、swiftが強力な言語であるため、jsonデータのタイプがmodelプロパティのタイプに対応していることに注意してください.そうしないと、解析に失敗します.
参照先:http://www.cocoachina.com/swift/20170630/19691.html
demo:https://github.com/NickQCX/CXMap/blob/master/README.md