欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Swift?中的?JSON?反序列化示例詳解

 更新時間:2022年07月20日 11:15:21   作者:網(wǎng)易云音樂技術(shù)團隊  
這篇文章主要為大家介紹了Swift中的JSON?反序列化示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

業(yè)界常用的幾種方案

手動解碼方案,如 Unbox(DEPRECATED)

Swift 早期普遍采用的方案,類似的還有 ObjectMapper

該方案需要使用者手動編寫解碼邏輯,使用成本比較高;目前已被 Swift 官方推出的 Codable 取代

示例:

struct User {
    let name: String
    let age: Int
}
extension User: Unboxable {
    init(unboxer: Unboxer) throws {
        self.name = try unboxer.unbox(key: "name")
        self.age = try unboxer.unbox(key: "age")
    }
}

阿里開源的 HandyJSON

HandyJSON

HandyJSON 目前依賴于從 Swift Runtime 源碼中推斷的內(nèi)存規(guī)則,直接對內(nèi)存進行操作。

在使用方面,不需要繁雜的定義,不需要繼承自 NSObject,聲明實現(xiàn)了協(xié)議即可

示例:

class Model: HandyJSON {
    var userId: String = ""
    var nickname: String = ""
    required init() {}
}
let jsonObject: [String: Any] = [
    "userId": "1234",
    "nickname": "lilei",
] 
let model = Model.deserialize(from: object)

但是存在兼容和安全方面的問題,由于強依賴內(nèi)存布局規(guī)則,Swift 大版本升級時可能會有穩(wěn)定性問題。同時由于要在運行時通過反射解析數(shù)據(jù)結(jié)構(gòu),會對性能有一定影響

基于 Sourcery 的元編程方案

Sourcery是一款 Swift 代碼生成器,使用 SourceKitten 解析 Swift 源碼,根據(jù) Stencil 模版生成最終代碼

可定制能力非常強,基本可以滿足我們所有的需求

示例:

定義了 AutoCodable 協(xié)議,并且讓需要被解析的數(shù)據(jù)類型遵循該協(xié)議

protocol AutoCodable: Codable {}
class Model: AutoCodable {
    // sourcery: key = "userID"
    var userId: String = ""
    var nickname: String = ""
    required init(from decoder: Decoder) throws {
        try autoDecodeModel(from: decoder)
    }
}

之后通過 Sourcery 生成代碼,這個過程 Sourcery 會掃描所有代碼,對實現(xiàn)了 AutoCodable 協(xié)議的類/結(jié)構(gòu)體自動生成解析代碼

// AutoCodable.generated.swift
// MARK: - Model Codable
extension Model {
    enum CodingKeys: String, CodingKey {
        case userId = "userID"
        case nickname
    }
    // sourcery:inline:Model.AutoCodable
    public func autoDecodeModel(from decoder: Decoder) throws {
        // ...
    }
}

如上所示,還可以通過代碼注釋(注解)來實現(xiàn)鍵值映射等自定義功能,但是需要對使用者有較強的規(guī)范要求。其次在組件化過程中需要對每個組件進行侵入/改造,內(nèi)部團隊可以通過工具鏈解決,作為跨團隊通用方案可能不是太合適

Swift build-in API Codable

Swift 4.0 之后官方推出的 JSON 序列化方案,可以理解為 Unbox+Sourcery 的組合,編譯器會根據(jù)數(shù)據(jù)結(jié)構(gòu)定義,自動生成編解碼邏輯,開發(fā)者使用特定的 Decoder/Encoder 對數(shù)據(jù)進行轉(zhuǎn)化處理。

Codable 作為 Swift 官方推出的方案,使用者可以無成本的接入。不過在具體實踐過程中,碰到了一些問題

Key 值映射不友好,例如以下情況:

// swift
struct User: Codable {
    var name: String
    var age: Int
    // ...
}
// json1
{
  "name": "lilei"
}
// json2
{
  "nickname": "lilei"
}
// json3
{
  "nickName": "lilei"
}

Swift 編譯器會自動幫我們生成完整的 CodingKeys,但是如果需要將 json 中的 nickname 或 nickName 解析為 User.name 時,需要重寫整個 CodingKeys,包括其他無關(guān)屬性如 age

容錯處理能力不足、無法提供默認值

Swift 設(shè)計初衷之一就是安全性,所以對于一些類型的強校驗從設(shè)計角度是合理的,不過對于實際使用者來說會增加一些使用成本

舉個例子:

enum City: String, Codable {
    case beijing
    case shanghai
    case hangzhou
}
struct User: Codable {
    var name: String
    var city: City?
}
// json1
{
  "name": "lilei",
  "city": "hangzhou"
}
// json2
{
  "name": "lilei"
}
// json3
{
  "name": "lilei",
  "city": "shenzhen"
}
let decoder = JSONDecoder()
try {
  let user = try? decoder.decode(User.self, data: jsonData3)
}
catch {
  // json3 格式會進入該分支
  print("decode user error")
}

上述代碼中,json1 和 json2 可以正確反序列化成 User 結(jié)構(gòu),json3 由于 “shenzhen” 無法轉(zhuǎn)化成 City,導(dǎo)致整個 User 結(jié)構(gòu)解析失敗,而不是 name 解析成功,city 失敗后變成 nil

  • 嵌套結(jié)構(gòu)解析繁瑣
  • JSONDecoder 只接受 data,不支持 dict,特殊場景使用時的類型轉(zhuǎn)化存在性能損耗

屬性裝飾器,如 BetterCodable

BetterCodable

Swift 5.0 新增的語言特性,通過該方案可以補足原生 Codable 方案一些補足之處,比如支持默認值、自定義解析兜底策略等,具體原理也比較簡單,有興趣的可自行了解

示例:

struct UserPrivilege: Codable {
    @DefaultFalse var isAdmin: Bool
}
let json = #"{ "isAdmin": null }"#.data(using: .utf8)!
let result = try JSONDecoder().decode(Response.self, from: json)
print(result) // UserPrivilege(isAdmin: false)

不過在實際編碼中,需要對數(shù)據(jù)結(jié)構(gòu)的屬性顯式描述,增加了使用成本

各個方案優(yōu)缺點對比

 CodableHandyJSONBetterCodableSourcery
類型兼容????
支持默認值????
鍵值映射????
接入/使用成本????
安全性????
性能????

上述方案都有各自的優(yōu)缺點,基于此我們希望找到更適合云音樂的方案。從使用接入和使用成本上來說,Codable 無疑是最佳選擇,關(guān)鍵點在于如何解決存在的問題

Codable 介紹

原理淺析

先看一組數(shù)據(jù)結(jié)構(gòu)定義,該數(shù)據(jù)結(jié)構(gòu)遵循 Codable 協(xié)議

enum Gender: Int, Codable {
    case unknown
    case male
    case female
}
struct User: Codable {
    var name: String
    var age: Int
    var gender: Gender
}

使用命令 swiftc main.swift -emit-sil | xcrun swift-demangle > main.sil 生成 SIL(Swift Intermediate Language),分析一下編譯器具體做了哪些事情

可以看到編譯器會自動幫我們生成 CodingKeys 枚舉和 init(from decoder: Decoder) throws 方法

enum Gender : Int, Decodable & Encodable {
  case unknown
  case male
  case female
  init?(rawValue: Int)
  typealias RawValue = Int
  var rawValue: Int { get }
}
struct User : Decodable & Encodable {
  @_hasStorage var name: String { get set }
  @_hasStorage var age: Int { get set }
  @_hasStorage var gender: Gender { get set }
  enum CodingKeys : CodingKey {
    case name
    case age
    case gender
    @_implements(Equatable, ==(_:_:)) static func __derived_enum_equals(_ a: User.CodingKeys, _ b: User.CodingKeys) -> Bool
    func hash(into hasher: inout Hasher)
    init?(stringValue: String)
    init?(intValue: Int)
    var hashValue: Int { get }
    var intValue: Int? { get }
    var stringValue: String { get }
  }
  func encode(to encoder: Encoder) throws
  init(from decoder: Decoder) throws
  init(name: String, age: Int, gender: Gender)
}

下面摘錄了部分用于解碼的 SIL 片段,不熟悉的讀者可以跳過該部分,直接看后面轉(zhuǎn)譯過的偽代碼

// User.init(from:)
sil hidden [ossa] @$s6source4UserV4fromACs7Decoder_p_tKcfC : $@convention(method) (@in Decoder, @thin User.Type) -> (@owned User, @error Error) {
// %0 "decoder"                                   // users: %83, %60, %8, %5
// %1 "$metatype"
bb0(%0 : $*Decoder, %1 : $@thin User.Type):
  %2 = alloc_box ${ var User }, var, name "self"  // user: %3
  %3 = mark_uninitialized [rootself] %2 : ${ var User } // users: %84, %61, %4
  %4 = project_box %3 : ${ var User }, 0          // users: %59, %52, %36, %23
  debug_value %0 : $*Decoder, let, name "decoder", argno 1, implicit, expr op_deref // id: %5
  debug_value undef : $Error, var, name "$error", argno 2 // id: %6
  %7 = alloc_stack [lexical] $KeyedDecodingContainer<User.CodingKeys>, let, name "container", implicit // users: %58, %57, %48, %80, %79, %33, %74, %73, %20, %69, %68, %12, %64
  %8 = open_existential_addr immutable_access %0 : $*Decoder to $*@opened("6CB1A110-E4DA-11EC-8A4C-8A05F3D75FB2") Decoder // users: %12, %12, %11
  %9 = metatype $@thin User.CodingKeys.Type
  %10 = metatype $@thick User.CodingKeys.Type     // user: %12
  %11 = witness_method $@opened("6CB1A110-E4DA-11EC-8A4C-8A05F3D75FB2") Decoder, #Decoder.container : <Self where Self : Decoder><Key where Key : CodingKey> (Self) -> (Key.Type) throws -> KeyedDecodingContainer<Key>, %8 : $*@opened("6CB1A110-E4DA-11EC-8A4C-8A05F3D75FB2") Decoder : $@convention(witness_method: Decoder) <τ_0_0 where τ_0_0 : Decoder><τ_1_0 where τ_1_0 : CodingKey> (@thick τ_1_0.Type, @in_guaranteed τ_0_0) -> (@out KeyedDecodingContainer<τ_1_0>, @error Error) // type-defs: %8; user: %12
  try_apply %11<@opened("6CB1A110-E4DA-11EC-8A4C-8A05F3D75FB2") Decoder, User.CodingKeys>(%7, %10, %8) : $@convention(witness_method: Decoder) <τ_0_0 where τ_0_0 : Decoder><τ_1_0 where τ_1_0 : CodingKey> (@thick τ_1_0.Type, @in_guaranteed τ_0_0) -> (@out KeyedDecodingContainer<τ_1_0>, @error Error), normal bb1, error bb5 // type-defs: %8; id: %12
bb1(%13 : $()):                                   // Preds: bb0
  %14 = metatype $@thin String.Type               // user: %20
  %15 = metatype $@thin User.CodingKeys.Type
  %16 = enum $User.CodingKeys, #User.CodingKeys.name!enumelt // user: %18
  %17 = alloc_stack $User.CodingKeys              // users: %22, %20, %67, %18
  store %16 to [trivial] %17 : $*User.CodingKeys  // id: %18
  // function_ref KeyedDecodingContainer.decode(_:forKey:)
  %19 = function_ref @$ss22KeyedDecodingContainerV6decode_6forKeyS2Sm_xtKF : $@convention(method) <τ_0_0 where τ_0_0 : CodingKey> (@thin String.Type, @in_guaranteed τ_0_0, @in_guaranteed KeyedDecodingContainer<τ_0_0>) -> (@owned String, @error Error) // user: %20
  try_apply %19<User.CodingKeys>(%14, %17, %7) : $@convention(method) <τ_0_0 where τ_0_0 : CodingKey> (@thin String.Type, @in_guaranteed τ_0_0, @in_guaranteed KeyedDecodingContainer<τ_0_0>) -> (@owned String, @error Error), normal bb2, error bb6 // id: %20
// %21                                            // user: %25
bb2(%21 : @owned $String):                        // Preds: bb1
  dealloc_stack %17 : $*User.CodingKeys           // id: %22
  %23 = begin_access [modify] [unknown] %4 : $*User // users: %26, %24
  %24 = struct_element_addr %23 : $*User, #User.name // user: %25
  assign %21 to %24 : $*String                    // id: %25
  end_access %23 : $*User                         // id: %26
  ...

大致上就是從 decoder 中獲取 container,在通過 decode 方法解析出具體的值,翻譯成對應(yīng)的 Swift 代碼如下:

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: User.CodingKeys.Type)
    self.name = try container.decode(String.self, forKey: .name)
    self.age = try container.decode(Int.self, forKey: .age)
    self.gender = try container.decode(Gender.self, forKey: .gender)
}

由此可見反序列化中關(guān)鍵部分就在 Decoder 上,平常使用較多的 JSONDecoder 就是對 Decoder 協(xié)議的一種實現(xiàn)

編譯器自動生成的代碼我們無法人工干預(yù),如果想要讓反序列化結(jié)果達到我們的預(yù)期,需要定制化實現(xiàn)一個 Decoder

Swift 標(biāo)準庫部分是開源的,有興趣的同學(xué)可移步 JSONDecoder.swift

Decoder、Container 協(xié)議

public protocol Decoder {
    var codingPath: [CodingKey] { get }
    var userInfo: [CodingUserInfoKey : Any] { get }
    func container&lt;Key&gt;(keyedBy type: Key.Type) throws -&gt; KeyedDecodingContainer&lt;Key&gt; where Key : CodingKey
    func unkeyedContainer() throws -&gt; UnkeyedDecodingContainer
    func singleValueContainer() throws -&gt; SingleValueDecodingContainer
}

Decoder 包含了 3 種類型的容器,具體關(guān)系如下

容器需要實現(xiàn)各自的 decode 方法,進行具體的解析工作

KeyedDecodingContainerProtocol - 鍵值對字典容器協(xié)議(KeyedDecodingContainer 用于類型擦除)

func decodeNil(forKey key: Self.Key) throws -> Bool
func decode(_ type: Bool.Type, forKey key: Self.Key) throws -> Bool
func decode(_ type: String.Type, forKey key: Self.Key) throws -> String
...
func decodeIfPresent(_ type: Bool.Type, forKey key: Self.Key) throws -> Bool?
func decodeIfPresent(_ type: String.Type, forKey key: Self.Key) throws -> String?
...

SingleValueDecodingContainer - 單值容器協(xié)議

func decode(_ type: UInt8.Type) throws -> UInt8
...
func decode<T>(_ type: T.Type) throws -> T where T : Decodable

UnkeyedDecodingContainer - 數(shù)組容器協(xié)議

mutating func decodeNil() throws -> Bool
mutating func decode(_ type: Int64.Type) throws -> Int64
mutating func decode(_ type: String.Type) throws -> String
...
mutating func decodeIfPresent(_ type: Bool.Type) throws -> Bool?
mutating func decodeIfPresent(_ type: String.Type) throws -> String?

典型的 JSONDecoder 使用姿勢

let data = ...
let decoder = JSONDecoder()
let user = try? decoder.decode(User.self, from: data)

解析流程如下:

Decoder 的核心解析邏輯都在 Container 內(nèi)部,下面會根據(jù)我們的需求,對該部分邏輯進行設(shè)計與實現(xiàn)

自研方案

功能設(shè)計

首先需要明確我們最終需要的效果

  • 支持默認值
  • 類型互相兼容,如 JSON 中的 int 類型可以被正確的解析為 Model 中的 String 類型
  • 解碼失敗允許返回 nil ,而不是直接判定解碼過程失敗
  • 支持 key 映射
  • 支持自定義解碼邏輯

這里定義以下幾個協(xié)議

默認值協(xié)議,默認實現(xiàn)了常見類型的缺省值,自定義類型也可以按需實現(xiàn)

public protocol NECodableDefaultValue {
    static func codableDefaultValue() -> Self
}
extension Bool: NECodableDefaultValue {
    public static func codableDefaultValue() -> Self { false }
}
extension Int: NECodableDefaultValue {
    public static func codableDefaultValue() -> Self { 0 }
}
...

key 值映射協(xié)議

public protocol NECodableMapperValue {
    var mappingKeys: [String] { get }
}
extension String: NECodableMapperValue {
    public var mappingKeys: [String] {
        return [self]
    }
}
extension Array: NECodableMapperValue where Element == String {
    public var mappingKeys: [String] {
        return self
    }
}

Codable 協(xié)議擴展

public protocol NECodable: Codable {
    // key 值映射關(guān)系定義,類似 YYModel 功能
    static var modelCustomPropertyMapper: [String: NECodableMapperValue]? { get }
    // 除了 NECodableDefaultValue 返回的默認值,還可以在該函數(shù)中定義默認值
    static func decodingDefaultValue<CodingKeys: CodingKey>(for key: CodingKeys) -> Any?
    // 在解析完數(shù)據(jù)結(jié)構(gòu)之后,提供二次修改的機會
    mutating func decodingCustomTransform(from jsonObject: Any, decoder: Decoder) throws -> Bool
}

最終的使用姿勢

struct Model: NECodable {
    var nickName: String
    var age: Int
    static var modelCustomPropertyMapper: [String : NECodableMapperValue]? = [
        "nickName": ["nickname", "nickName"],
        "age": "userInfo.age"
    ]
    static func decodingDefaultValue<CodingKeys>(for key: CodingKeys) -> Any? where CodingKeys : CodingKey {
        guard let key = key as? Self.CodingKeys else { return nil }
        switch key {
        case .age:
            // 提供默認年齡
            return 18
        default:
            return nil
        }
    }
}
let jsonObject: [String: Any] = [
    "nickname": "lilei",
    "userInfo": [
        "age": 123
    ],
]
let model = try NEJSONDecoder().decode(Model.self, jsonObject: jsonObject)
XCTAssert(model.nickName == "lilei")
XCTAssert(model.age == 123)

Decoder、Container 具體實現(xiàn)

定義類 NEJSONDecoder 作為 Decoder 協(xié)議的具體實現(xiàn),同時還要實現(xiàn)三個容器協(xié)議

在容器內(nèi)部需要實現(xiàn)大量的 decode 方法用于解析具體值,我們可以抽象一個工具類,進行相應(yīng)的類型解析、轉(zhuǎn)換、提供默認值等功能

下面給出一部分 keyedContainer 實現(xiàn),大致流程如下:

  • 先調(diào)用的 entry 方法,該方法根據(jù) key、keyMapping 從 JSON 中獲取原始值
  • 通過 unbox 方法,將原始值(可能是 String、Int 類型)轉(zhuǎn)化成預(yù)期類型(比如 Bool)
  • 如果上述過程失敗,則進入默認值處理流程

    首先通過模型定義的 decodingDefaultValue 方法獲取默認值,如果未獲取到進行步驟 b

    通過 NECodableDefaultValue 協(xié)議獲取類型的默認值

  • 解析完成
class NEJSONKeyedDecodingContainer<K : CodingKey> : KeyedDecodingContainerProtocol {
		public func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool {
        do {
            return try _decode(type, forKey: key)
        }
        catch {
            if let value = self.defaultValue(for: key),
               let unbox = try? decoder.unbox(value, as: Bool.self) { return unbox }
            if self.provideDefaultValue {
                return Bool.codableDefaultValue()
            }
            throw error
        }
    }
		public func _decode(_ type: Bool.Type, forKey key: Key) throws -> Bool {
        guard let entry = self.entry(for: key) else {
            throw ...
        }
        self.decoder.codingPath.append(key)
        defer { self.decoder.codingPath.removeLast() }
        guard let value = try self.decoder.unbox(entry, as: Bool.self) else {
            throw ...
        }
        return value
    }
}

再議 PropertyWrapper

在 NECodable 協(xié)議中,保留了 YYModel 的使用習(xí)慣,key 映射以及默認值提供需要單獨實現(xiàn) NECodable 協(xié)議的兩個方法

而利用 Swift 的屬性裝飾器,可以讓開發(fā)者更加便捷的實現(xiàn)上述功能:

@propertyWrapper
class NECodingValue<Value: Codable>: Codable {
    public convenience init(wrappedValue: Value) {
        self.init(storageValue: wrappedValue, keys: nil)
    }
    public convenience init(wrappedValue: Value, keys: String...) {
        self.init(storageValue: wrappedValue, keys: keys)
    }
    public convenience init<T>(wrappedValue: Optional<T> = .none, keys: String...) where Value == Optional<T> {
        self.init(storageValue: wrappedValue, keys: [])
    }
    public convenience init(keys: String...) {
        self.init(keys: keys)
    }
    // ....
}
struct Model: NECodable {
    @NECodingValue(keys: "nickname")
    var name: String
    // JSON 中不存在時,默認為 hangzhou
    @NECodingValue
    var city: String = "hangzhou"
    // JSON 中不存在時,默認為 false
    var enable: Bool
}

實現(xiàn)方式比較取巧:

通過屬性修飾器包裝實例變量,NECodingValue(keys: "nickname") 實例最先被初始化,其中包含我們定義的 keys、wrapperValue,而后的 init(from decoder: Decoder) 過程又通過 decoder 生成 NECodingValue(from: decoder) 變量并賦值給 _name 屬性,此時第一個 NECodingValue 變量就會被釋放,從而獲得了一個代碼執(zhí)行時機,用來進行定制的解碼流程(將 defaultValue 復(fù)制過來,使用自定義的 key 進行解碼等等…)

應(yīng)用場景示例

反序列化通常用于處理服務(wù)端返回的數(shù)據(jù),基于 Swift 的語法特性,我們可以非常簡單的定義一個網(wǎng)絡(luò)請求協(xié)議,舉個例子:

網(wǎng)絡(luò)請求協(xié)議

protocol APIRequest {
    associatedtype Model
    var path: String { get }
    var parameters: [String: Any]? { get }
    static func parse(_ data: Any) throws -> Model
}
// 缺省實現(xiàn)
extension APIRequest {
    var parameters: [String: Any]? { nil }
    static func parse(_ data: Any) throws -> Model {
        throw APIError.dataExceptionError()
    }
}

擴展 APIRequest 協(xié)議,通過 Swift 的類型匹配模式,自動進行反序列化

extension APIRequest where Model: NECodable {
    static func parse(_ data: Any) throws -> Model {
        let decoder = NEJSONDecoder()
        return try decoder.decode(Model.self, jsonObject: data)
    }
}

擴展 APIRequest 協(xié)議,增加網(wǎng)絡(luò)請求方法

extension APIRequest {
    @discardableResult
    func start(completion: @escaping (Result<Model, APIError>) -> Void) -> APIToken<Self> {
        // 具體的網(wǎng)絡(luò)請求流程,基于底層網(wǎng)絡(luò)庫實現(xiàn)
    }
}

最終業(yè)務(wù)側(cè)可以非常簡單的定義一個網(wǎng)絡(luò)接口,并發(fā)起請求

// 網(wǎng)絡(luò)接口定義
struct MainRequest: APIRequest {
    struct Model: NECodable {
        struct Item: NECodable {
            var title: String
        }
        var items: [Item]
        var page: Int
    }
    let path = "/api/main"
}
// 業(yè)務(wù)側(cè)發(fā)起網(wǎng)絡(luò)請求
func doRequest() {
    MainRequest().start { result in
        switch result {
            case .success(let model):
                // to do something
                print("page index: (model.page)")
            case .failure(let error):
                HUD.show(error: error)
        }
    }
}

單元測試

序列化/反序列化過程會存在很多邊界情況,需要針對各場景構(gòu)造單元測試,確保所有行為符合預(yù)期

性能對比

上圖是各反序列化庫執(zhí)行 10000 次后得到的結(jié)果,可能看到從 Data 數(shù)據(jù)轉(zhuǎn)換為 Model 時 JSONDecoder 性能最佳,從 JSON Object 傳換為 Model 時 NEJSONDecoder 性能最佳,HandyJSON 耗時均最長

測試代碼:

import XCTest
@testable import JSONPerformance
import NEAutoCodable
import HandyJSON
class JSONPerformanceTests: XCTestCase {
    override func setUpWithError() throws {
        // Put setup code here. This method is called before the invocation of each test method in the class.
    }
    override func tearDownWithError() throws {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
    }
    func jsonObject() -> [String: Any] {
        let object: [String: Any] = [
            "userId": "12345",
            "nickname": "用戶1",
            "avatarImgUrl": "http://baidu.com/avatarImageUrl.jpg",
            "signature": "qqq",
            "age": 19,
            "birthday": 1052209042000,
            "gender": 2,
            "constellation": "金牛座",
            "imAccId": "abcdefgzzzzzzzzz",
            "realMan": 1,
            "realPersonGender": 0,
            "registerTime": 1620289047216,
            "language": "en-US",
        ]
        return object
    }
    func jsonData() -> Data {
        return try! JSONSerialization.data(withJSONObject: jsonObject())
    }
    class Codable_Model: NECodable {
        var userId: String
        var nickname: String
        var avatarImgNosKey: String?
        var avatarImgUrl: String
        var signature: String
        var age: Int
        var birthday: Int
        var gender: Int
        var constellation: String
        var imAccId: String
        var realMan: Int
        var realPersonGender: Int
        var registerTime: Int
        var language: String
    }
    class NECodable_Model: NECodable {
        var userId: String
        var nickname: String
        var avatarImgNosKey: String?
        var avatarImgUrl: String
        var signature: String
        var age: Int
        var birthday: Int
        var gender: Int
        var constellation: String
        var imAccId: String
        var realMan: Int
        var realPersonGender: Int
        var registerTime: Int
        var language: String
    }
    class HandyJSON_Model: HandyJSON {
        var userId: String = ""
        var nickname: String = ""
        var avatarImgNosKey: String?
        var avatarImgUrl: String = ""
        var signature: String = ""
        var age: Int = 0
        var birthday: Int = 0
        var gender: Int = 0
        var constellation: String = ""
        var imAccId: String = ""
        var realMan: Int = 0
        var realPersonGender: Int = 0
        var registerTime: Int = 0
        var language: String = ""
        required init() {}
    }
    let loopCount = 10000
    // 0.128
    func testDataJSONDecoder() throws {
        self.measure {
            let decoder = JSONDecoder()
            let data = jsonData()
            for _ in 0..<loopCount {
                let model = try! decoder.decode(Codable_Model.self, from: data)
            }
        }
    }
    // 0.196
    func testObjectJSONDecoder() throws {
        // This is an example of a performance test case.
        self.measure {
            let decoder = JSONDecoder()
            let object = jsonObject()
            for _ in 0..<loopCount {
                let data = try! JSONSerialization.data(withJSONObject: object)
                let model = try! decoder.decode(Codable_Model.self, from: data)
            }
        }
    }
    // 0.251
    func testDataNEJSONDecoder() throws {
        self.measure {
            let decoder = NEJSONDecoder()
            let data = jsonData()
            for _ in 0..<loopCount {
                let model = try! decoder.decode(NECodable_Model.self, data: data)
            }
        }
    }
    // 0.166
    func testObjectNEJSONDecoder() throws {
        self.measure {
            let decoder = NEJSONDecoder()
            let object = jsonObject()
            for _ in 0..<loopCount {
                let model = try! decoder.decode(NECodable_Model.self, jsonObject: object)
            }
        }
    }
    // 0.440
    func testDataHandyJSON() throws {
        self.measure {
            let data = jsonData()
            for _ in 0..<loopCount {
                let object = try! JSONSerialization.jsonObject(with: data) as! [String: Any]
                let model = HandyJSON_Model.deserialize(from: object)!
            }
        }
    }
    // 0.335
    func testObjectHandyJSON() throws {
        self.measure {
            let object = jsonObject()
            for _ in 0..<loopCount {
                let model = HandyJSON_Model.deserialize(from: object)
            }
        }
    }
}

以上就是Swift 中的 JSON 反序列化示例詳解的詳細內(nèi)容,更多關(guān)于Swift JSON反序列的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 深入解析Swift編程中的構(gòu)造方法

    深入解析Swift編程中的構(gòu)造方法

    先進的Swfit語言同樣具有構(gòu)造方法,構(gòu)造方法在對象被創(chuàng)建后會首先被調(diào)用,這里我們就來深入解析Swift編程中的構(gòu)造方法:
    2016-07-07
  • 淺析Swift中struct與class的區(qū)別(匯編角度底層分析)

    淺析Swift中struct與class的區(qū)別(匯編角度底層分析)

    這篇文章主要介紹了Swift中struct與class的區(qū)別 ,本文從匯編角度分析struct與class的區(qū)別,通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-03-03
  • Compose聲明式代碼語法對比React?Flutter?SwiftUI

    Compose聲明式代碼語法對比React?Flutter?SwiftUI

    這篇文章主要為大家介紹了Compose聲明式代碼語法對比React?Flutter?SwiftUI來解釋為什么說?Compose?的聲明式代碼最簡潔,有需要的朋友可以借鑒參考下
    2022-08-08
  • 在Swift中使用Objective-C編寫類、繼承Objective-C類

    在Swift中使用Objective-C編寫類、繼承Objective-C類

    這篇文章主要介紹了在Swift中使用Objective-C編寫類、繼承Objective-C類等操作方法介紹,需要的朋友可以參考下
    2014-07-07
  • Swift利用CoreData實現(xiàn)一個上班簽到的小工具

    Swift利用CoreData實現(xiàn)一個上班簽到的小工具

    這篇文章主要給大家介紹了關(guān)于Swift利用CoreData實現(xiàn)一個上班簽到小工具的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-12-12
  • 因為一個Crash引發(fā)對Swift構(gòu)造器的思考分析

    因為一個Crash引發(fā)對Swift構(gòu)造器的思考分析

    這篇文章主要給大家介紹了關(guān)于因為一個Crash引發(fā)對Swift構(gòu)造器的思考分析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者使用Swift具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • SwiftUI圖片縮放、拼圖等處理教程

    SwiftUI圖片縮放、拼圖等處理教程

    SwiftUI是一種使用Swift語言在蘋果設(shè)備上構(gòu)建用戶界面的創(chuàng)新且簡單的方式,下面這篇文章主要給大家介紹了關(guān)于SwiftUI圖片縮放、拼圖等處理的相關(guān)資料,需要的朋友可以參考下
    2021-08-08
  • Swift中轉(zhuǎn)義閉包示例詳解

    Swift中轉(zhuǎn)義閉包示例詳解

    在Swift 中的閉包類似于結(jié)構(gòu)塊,并可以在任何地方調(diào)用,下面這篇文章主要給大家介紹了關(guān)于Swift中轉(zhuǎn)義閉包的相關(guān)資料,需要的朋友可以參考下
    2021-11-11
  • Swift 4中一些實用的數(shù)組技巧小結(jié)

    Swift 4中一些實用的數(shù)組技巧小結(jié)

    這篇文章主要給大家分享了關(guān)于Swift 4中一些實用的數(shù)組技巧,文中通過示例代碼介紹的介紹的非常詳細,對大家學(xué)習(xí)或者使用swift具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-03-03
  • 升級到Swift 4.0可能遇到的坑總結(jié)

    升級到Swift 4.0可能遇到的坑總結(jié)

    這篇文章主要給大家介紹了關(guān)于升級到Swift 4.0可能遇到的坑的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用swift4具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-11-11

最新評論