Error[8]: Undefined offset: 63, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述在iOS项目开发过程中,我们经常会用到将从服务器获取的 json 转 model 的 *** 作,我们可以使用 Swift 提供的setValuesForKeys 或者 Objective-C 提供的setValuesForKeysWithDictionary 方法来完成这一 *** 作。 使用上面两个方法只能将字典转换成 model , 如果 json 最外层是个数组,那么我们就必须在循环中使用这个方法,这非常

在iOS项目开发过程中,我们经常会用到将从服务器获取的 Json 转 model 的 *** 作,我们可以使用 Swift 提供的setValuesForKeys 或者 Objective-C 提供的setValuesForKeysWithDictionary 方法来完成这一 *** 作。

使用上面两个方法只能将字典转换成 model,如果 Json 最外层是个数组,那么我们就必须在循环中使用这个方法,这非常不方便, 而且还有个条件,就是 model 中的所有属性名必须跟字典中的 key 完全对应,这样就会遇到另外一个问题,如果我们字典中的一个 key 与系统关键字重名,那我们在 model 就不能使用这个 key 作为属性名了。

为了解决上面的问题,我们会使用一些第三方库去完成字典转模型的 *** 作,例如 MJExtension 。由于它是一个 OC 的库,所以在 Swift 项目中需要引入桥接文件。在 Swift 中使用其 API 时其实是很不 swift 的。所以现在我们就用 Swift 3.0 来写一个 swift style 的 Json 转模型的库吧。

例如我们有这样的两个 model:

class User: NSObject {    var name: String?    var age = 0    var desc: String?}class Repos: NSObject {    var Title: String?    var owner: User?    var vIEwers: [User]?}

最终我们想实现这样的调用:

let repos = Json ~> Repos.self    // 将一个字典转成一个Repos的实例 let vIEwers  = vIEwers => User.self  //将一个数组转换成User的数组

~>=> 是自定义的运算符,主要是为了调用方便。它们的定义是这样的:

public func ~><T: NSObject>(lhs: Any,rhs: T.Type) -> T?public func =><T: NSObject>(lhs: Any,rhs: T.Type) -> [T]?

这里给出我的实现 ModelSwift。大家可以先看看我的实现然后试着写出自己的实现。好了,现在就让我们开始吧。

要解决的问题

由于将数组转成模型数组,其实要做的工作跟将字典转模型是一样的,只是做了个循环而已。所以我们首先要解决的问题是:如何在 Swift 将字典转成模型。这里我们是使用 KVC就可以了。我们使用 NSObject 的 setValue(_ value: Any?,forKey key: String) 方法来给对象设置值。

从上面要实现的效果来看,我们在使用前并不用先实例化一个对象。所以我们要解决的第二个问题是:如何通过类型来实例化一个对象。

另一个要解决的问题是字典中的 key 与关键字重名,或者我们想使用自己的名字。所以我们要实现自己的映射的策略。

还有一个问题是,如果我们服务器返回的字典数据中包含另外一个字典数组,对应我们的 model 中就是一个对象包含另外一个对象的数组。那么我们怎样才能知道这个数组中对象的类型呢?

实现思路

对于上面提到的第一问题我在上面已经给出了解决方案,就是让我们的 model 继承 NSObject,然后使用 setValue(_ value: Any?,forKey key: String) 方法来给对象设置值。这里的 value 其实是要根据 model 中的属性名去字典中获取的。如果我们能拿到 model 所有的属性名,那么给 model 设置值的 *** 作就完成了。那么如何获取到 model 的属性名呢?这就必须得使用到 Swift 中的反射机制了。

Mirror

Swift 的反射机制是基于一个叫 Mirror 的 struct 来实现的。对于 Mirror 的详细结构大家可以按住 cmd 点进去查看。这里我们主要关注的是 public typealias Child = (label: String?,value: Any) 这个 typealias,它其实是一个元祖,label 就表示我们的属性名,是 Optional 的。value 表示的是属性的值。这里 label 为什么是 Optional 的?如果你仔细考虑下,其实这是非常有意义的,并不是所有支持反射的数据结构都包含有名字的子节点。 Mirror 会以属性的名字做为 label,但是 Collection 只有下标,没有名字。Tuple 同样也可能没有给它们的条目指定名字。

Mirror 有个 children 的存储属性,它的定义是这样的:

public let children: Mirror.Children

这里的 Mirror.Children 也是一个 typealias,它是这样定义的:

public typealias Children = AnyCollection<Mirror.Child>

可以看到它是 Child 的集合。所以我们可以通过 Mirror 的 children 属性来获得 model 的所有属性名。

我们写个类来测试一下:

class Person: NSObject {    var name = ""    var age = 0    var frIEnds: [Person]?}let mirror = Mirror(reflecting: Person())for case let (label?,value) in mirror.children {    print ("\(label) = \(value)")}

运行结果是如下:

name = age = 0frIEnds = nil

Mirror 还有一个类型为 Any.TypesubjectType 存储属性,表示该映射对象的类型,例如上面的 mirror.subjectType 就是 User。使用 subjectType 就可以获得对象的类型以及其所有属性的类型。为了实现这个效果,我们可以写出下面的代码:

func subjectType(of subject: Any) -> Any.Type {    let mirror = Mirror(reflecting: subject)    return mirror.subjectType}func children(of subject: Any) {    let mirror = Mirror(reflecting: subject)    for case let(label?,value) in mirror.children {        print ("\(label) = \(subjectType(of: value))")    }}children(of: Person())

打印结果是这样的:

name = Stringage = IntfrIEnds = Optional<Array<Person>>

我原本想使用这个方法来得到 model 中包含的另外对象的类型和数组中对象的类型,例如 Person 中有 fatherfrIEnds 属性:

class Person: NSObject {    var name = ""    var age = 100    var father: Person?    var frIEnds: [Person]?}

但是发现结果是 Optional<Person>Optional<Array<Person>>。所以我们还是得显示地指出一个 model 中包含的其他对象的类型,以及数组中对象的类型。在后面我会给出自己的实现。大家可以给出自己的实现。

通过类型来实例化一个对象

要使用 Mirror 来获得反射对象的所有属性名,就必须先使用 init(reflecting subject: Any) 来创建一个 Mirror。而创建 Mirror 就必须传入一个 subject(在这里我们主要传入一个NSObject类型的对象)。所以我们的首要任务就是通过类型来实例化一个对象。

有些同学可能有疑问了:我要转换成 Person 的对象,我直接传入一个
Person 的实例就行了啊。如果你看看我们 josn 转模型的方法定义就能明白了。 func ~><T: NSObject>(lhs: Any,rhs: T.Type) -> T?

还是以上面的 Person 为例,我们看看这样的调用:

Person.self().age// 结果是:100

所以我们通过一个类的 self()方法可以得到一个类的实例。其实我们还可以通过 AnyClass 来实例化对象。AnyClass 是类的类型,其定义是这样的:

public typealias AnyClass = AnyObject.Type

我们通过类的self属性可以得到类的类型:

Person.self     //结果是:Person.Type

得到类的类型后,通过调用其 init()方法就可以创建一个实例了:

Person.self.init().age// 结果是:100

使用类型创建对象的类中的init方法前面必须是 required 的,因为这么创建方式是使用Meta type来创建的。由于我们 Json 转模型的 model 是继承自 NSObject 的,所以不用在每个类中显示地实现。

写个简单的 josn 转模型

有了上面的基础就可以来实现我们的 josn 转模型了。首先我们来写出 ~> 的定义,并通过类来创建一个对象

infix operator ~>func ~><T: NSObject>(lhs: Any,rhs: T.Type) -> T? {    guard let Json = lhs as? [String: Any],!Json.isEmpty else {        return nil    }        let obj = T.self()    let mirror = Mirror(reflecting: obj)        for case let(label?,value) in mirror.children {        print ("\(label) = \(value)")    }        return obj}class Person: NSObject {    var name = ""    var age = 0    overrIDe var description: String {        return "name = \(name),age = \(age)"    }}let Json: [String: Any] = ["name": "jewelz","age": 100]let p = Json ~> Person.self// 打印结果:// name = // age = 0

通过上面的几行代码我们确实成功的创建了一个 Person 的实例了。下一步就是给实例设置值了。我们在上面的 for 循环中添加如下代码:

// 从字典中获取值if let value = Json[label] {     obj.setValue(value,forKey: label)}

整个代码就是这样的:

infix operator ~>func ~><T: NSObject>(lhs: Any,_) in mirror.children {        // 从字典中获取值        if let value = Json[label] {            obj.setValue(value,forKey: label)        }    }    return obj}let p = Json ~> Person.selfprint(p!)//结果:name = jewelz,age = 100

有了上面 ~> 的实现,=> 的实现就很简单了:

infix operator =>func =><T: NSObject>(lhs: Any,rhs: T.Type) -> [T]? {    guard let array = lhs as? [Any],!array.isEmpty else {        return nil    }        return array.flatMap{ 
class User: NSObject {    var name: String?    var age = 0    var desc: String?}class Repos: NSObject {    var Title: String?    var owner: User?    var vIEwers: [User]?}
~> rhs }}

上面只是实现了一个简单 josn 转模型,其实在实际项目中要解决的问题还有很多。现在再来看看我在文章开头给出的 User 类和 Respo 类:

desc

只简单的用上面的实现是无法得到想要的结果的。对于 User 类来说,description 属性对应 Json 的

public protocol Reflectable: class {    var reflectedobject: [String: Any.Type] { get }}
key,所以我们还要进行 model 的属性与 Json 的键的映射。这里的思路就是将 model 的属性名作为 key,以要替换的 Json 的键作为 value 存入字典中。我们可以拓展 NSObject ,添加一个计算属性并提供一个空实现。不过这样的倾入性太大,毕竟不是所有的类都需要做这个映射。所以最后的方式是 POP。比如我们可以制定这样一个协议:

owner

在需要做映射的类中去实现该协议。

对于更复杂的 Repos 类来说,要做的事情更多。比如 owner的类型怎么知道?vIEwers 这个对象怎么完成赋值?Optional<User> 数组中的类型是什么,怎样才能完成赋值? 虽然通过上面提到的 Mirro 可以得到所有的类型,但得到的是 Optional<Array<User>>以及 setValue(_ value: Any?,forKey key: String)。我的解决的办法就跟上面做属性名替换是一样的。这里就不详细地说明了,大家可以各显神通。写出自己的实现。

写在最后

通过上面的几个步骤,我们就能很快的实现一个简单的 Json 转模型的需求了。总结起来就是以下几点:

所有要转换的 model 继承 NSObject

使用类的类型来实例化对象

通过反射获得对象的所有属性名

通过 [+++] 方法来给属性设置值

对于在最后提出的几个问题,我这里就不一一详细地说明了。大家可以点这里看看我的实现。大家可以使用 CocoaPods 或者 Carthage 将 ModelSwift 集成到项目中。如果在使用中有什么问题可以 issue 我,也可以给个 star 持续关注。

总结

以上是内存溢出为你收集整理的教你如何用Swift写个json转模型的开源库全部内容,希望文章能够帮你解决教你如何用Swift写个json转模型的开源库所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 165, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
教你如何用Swift写个json转模型的开源库_app_内存溢出

教你如何用Swift写个json转模型的开源库

教你如何用Swift写个json转模型的开源库,第1张

概述在iOS项目开发过程中,我们经常会用到将从服务器获取的 json 转 model 的 *** 作,我们可以使用 Swift 提供的setValuesForKeys 或者 Objective-C 提供的setValuesForKeysWithDictionary 方法来完成这一 *** 作。 使用上面两个方法只能将字典转换成 model , 如果 json 最外层是个数组,那么我们就必须在循环中使用这个方法,这非常

在iOS项目开发过程中,我们经常会用到将从服务器获取的 Json 转 model 的 *** 作,我们可以使用 Swift 提供的setValuesForKeys 或者 Objective-C 提供的setValuesForKeysWithDictionary 方法来完成这一 *** 作。

使用上面两个方法只能将字典转换成 model,如果 Json 最外层是个数组,那么我们就必须在循环中使用这个方法,这非常不方便, 而且还有个条件,就是 model 中的所有属性名必须跟字典中的 key 完全对应,这样就会遇到另外一个问题,如果我们字典中的一个 key 与系统关键字重名,那我们在 model 就不能使用这个 key 作为属性名了。

为了解决上面的问题,我们会使用一些第三方库去完成字典转模型的 *** 作,例如 MJExtension 。由于它是一个 OC 的库,所以在 Swift 项目中需要引入桥接文件。在 Swift 中使用其 API 时其实是很不 swift 的。所以现在我们就用 Swift 3.0 来写一个 swift style 的 Json 转模型的库吧。

例如我们有这样的两个 model:

class User: NSObject {    var name: String?    var age = 0    var desc: String?}class Repos: NSObject {    var Title: String?    var owner: User?    var vIEwers: [User]?}

最终我们想实现这样的调用:

let repos = Json ~> Repos.self    // 将一个字典转成一个Repos的实例 let vIEwers  = vIEwers => User.self  //将一个数组转换成User的数组

~>=> 是自定义的运算符,主要是为了调用方便。它们的定义是这样的:

public func ~><T: NSObject>(lhs: Any,rhs: T.Type) -> T?public func =><T: NSObject>(lhs: Any,rhs: T.Type) -> [T]?

这里给出我的实现 ModelSwift。大家可以先看看我的实现然后试着写出自己的实现。好了,现在就让我们开始吧。

要解决的问题

由于将数组转成模型数组,其实要做的工作跟将字典转模型是一样的,只是做了个循环而已。所以我们首先要解决的问题是:如何在 Swift 将字典转成模型。这里我们是使用 KVC就可以了。我们使用 NSObject 的 setValue(_ value: Any?,forKey key: String) 方法来给对象设置值。

从上面要实现的效果来看,我们在使用前并不用先实例化一个对象。所以我们要解决的第二个问题是:如何通过类型来实例化一个对象。

另一个要解决的问题是字典中的 key 与关键字重名,或者我们想使用自己的名字。所以我们要实现自己的映射的策略。

还有一个问题是,如果我们服务器返回的字典数据中包含另外一个字典数组,对应我们的 model 中就是一个对象包含另外一个对象的数组。那么我们怎样才能知道这个数组中对象的类型呢?

实现思路

对于上面提到的第一问题我在上面已经给出了解决方案,就是让我们的 model 继承 NSObject,然后使用 setValue(_ value: Any?,forKey key: String) 方法来给对象设置值。这里的 value 其实是要根据 model 中的属性名去字典中获取的。如果我们能拿到 model 所有的属性名,那么给 model 设置值的 *** 作就完成了。那么如何获取到 model 的属性名呢?这就必须得使用到 Swift 中的反射机制了。

Mirror

Swift 的反射机制是基于一个叫 Mirror 的 struct 来实现的。对于 Mirror 的详细结构大家可以按住 cmd 点进去查看。这里我们主要关注的是 public typealias Child = (label: String?,value: Any) 这个 typealias,它其实是一个元祖,label 就表示我们的属性名,是 Optional 的。value 表示的是属性的值。这里 label 为什么是 Optional 的?如果你仔细考虑下,其实这是非常有意义的,并不是所有支持反射的数据结构都包含有名字的子节点。 Mirror 会以属性的名字做为 label,但是 Collection 只有下标,没有名字。Tuple 同样也可能没有给它们的条目指定名字。

Mirror 有个 children 的存储属性,它的定义是这样的:

public let children: Mirror.Children

这里的 Mirror.Children 也是一个 typealias,它是这样定义的:

public typealias Children = AnyCollection<Mirror.Child>

可以看到它是 Child 的集合。所以我们可以通过 Mirror 的 children 属性来获得 model 的所有属性名。

我们写个类来测试一下:

class Person: NSObject {    var name = ""    var age = 0    var frIEnds: [Person]?}let mirror = Mirror(reflecting: Person())for case let (label?,value) in mirror.children {    print ("\(label) = \(value)")}

运行结果是如下:

name = age = 0frIEnds = nil

Mirror 还有一个类型为 Any.TypesubjectType 存储属性,表示该映射对象的类型,例如上面的 mirror.subjectType 就是 User。使用 subjectType 就可以获得对象的类型以及其所有属性的类型。为了实现这个效果,我们可以写出下面的代码:

func subjectType(of subject: Any) -> Any.Type {    let mirror = Mirror(reflecting: subject)    return mirror.subjectType}func children(of subject: Any) {    let mirror = Mirror(reflecting: subject)    for case let(label?,value) in mirror.children {        print ("\(label) = \(subjectType(of: value))")    }}children(of: Person())

打印结果是这样的:

name = Stringage = IntfrIEnds = Optional<Array<Person>>

我原本想使用这个方法来得到 model 中包含的另外对象的类型和数组中对象的类型,例如 Person 中有 fatherfrIEnds 属性:

class Person: NSObject {    var name = ""    var age = 100    var father: Person?    var frIEnds: [Person]?}

但是发现结果是 Optional<Person>Optional<Array<Person>>。所以我们还是得显示地指出一个 model 中包含的其他对象的类型,以及数组中对象的类型。在后面我会给出自己的实现。大家可以给出自己的实现。

通过类型来实例化一个对象

要使用 Mirror 来获得反射对象的所有属性名,就必须先使用 init(reflecting subject: Any) 来创建一个 Mirror。而创建 Mirror 就必须传入一个 subject(在这里我们主要传入一个NSObject类型的对象)。所以我们的首要任务就是通过类型来实例化一个对象。

有些同学可能有疑问了:我要转换成 Person 的对象,我直接传入一个
Person 的实例就行了啊。如果你看看我们 josn 转模型的方法定义就能明白了。 func ~><T: NSObject>(lhs: Any,rhs: T.Type) -> T?

还是以上面的 Person 为例,我们看看这样的调用:

Person.self().age// 结果是:100

所以我们通过一个类的 self()方法可以得到一个类的实例。其实我们还可以通过 AnyClass 来实例化对象。AnyClass 是类的类型,其定义是这样的:

public typealias AnyClass = AnyObject.Type

我们通过类的self属性可以得到类的类型:

Person.self     //结果是:Person.Type

得到类的类型后,通过调用其 init()方法就可以创建一个实例了:

Person.self.init().age// 结果是:100

使用类型创建对象的类中的init方法前面必须是 required 的,因为这么创建方式是使用Meta type来创建的。由于我们 Json 转模型的 model 是继承自 NSObject 的,所以不用在每个类中显示地实现。

写个简单的 josn 转模型

有了上面的基础就可以来实现我们的 josn 转模型了。首先我们来写出 ~> 的定义,并通过类来创建一个对象

infix operator ~>func ~><T: NSObject>(lhs: Any,rhs: T.Type) -> T? {    guard let Json = lhs as? [String: Any],!Json.isEmpty else {        return nil    }        let obj = T.self()    let mirror = Mirror(reflecting: obj)        for case let(label?,value) in mirror.children {        print ("\(label) = \(value)")    }        return obj}class Person: NSObject {    var name = ""    var age = 0    overrIDe var description: String {        return "name = \(name),age = \(age)"    }}let Json: [String: Any] = ["name": "jewelz","age": 100]let p = Json ~> Person.self// 打印结果:// name = // age = 0

通过上面的几行代码我们确实成功的创建了一个 Person 的实例了。下一步就是给实例设置值了。我们在上面的 for 循环中添加如下代码:

// 从字典中获取值if let value = Json[label] {     obj.setValue(value,forKey: label)}

整个代码就是这样的:

infix operator ~>func ~><T: NSObject>(lhs: Any,_) in mirror.children {        // 从字典中获取值        if let value = Json[label] {            obj.setValue(value,forKey: label)        }    }    return obj}let p = Json ~> Person.selfprint(p!)//结果:name = jewelz,age = 100

有了上面 ~> 的实现,=> 的实现就很简单了:

infix operator =>func =><T: NSObject>(lhs: Any,rhs: T.Type) -> [T]? {    guard let array = lhs as? [Any],!array.isEmpty else {        return nil    }        return array.flatMap{ 
class User: NSObject {    var name: String?    var age = 0    var desc: String?}class Repos: NSObject {    var Title: String?    var owner: User?    var vIEwers: [User]?}
~> rhs }}

上面只是实现了一个简单 josn 转模型,其实在实际项目中要解决的问题还有很多。现在再来看看我在文章开头给出的 User 类和 Respo 类:

desc

只简单的用上面的实现是无法得到想要的结果的。对于 User 类来说,description 属性对应 Json 的

public protocol Reflectable: class {    var reflectedobject: [String: Any.Type] { get }}
key,所以我们还要进行 model 的属性与 Json 的键的映射。这里的思路就是将 model 的属性名作为 key,以要替换的 Json 的键作为 value 存入字典中。我们可以拓展 NSObject ,添加一个计算属性并提供一个空实现。不过这样的倾入性太大,毕竟不是所有的类都需要做这个映射。所以最后的方式是 POP。比如我们可以制定这样一个协议:

owner

在需要做映射的类中去实现该协议。

对于更复杂的 Repos 类来说,要做的事情更多。比如 owner的类型怎么知道?vIEwers 这个对象怎么完成赋值?Optional<User> 数组中的类型是什么,怎样才能完成赋值? 虽然通过上面提到的 Mirro 可以得到所有的类型,但得到的是 Optional<Array<User>>以及 setValue(_ value: Any?,forKey key: String)。我的解决的办法就跟上面做属性名替换是一样的。这里就不详细地说明了,大家可以各显神通。写出自己的实现。

写在最后

通过上面的几个步骤,我们就能很快的实现一个简单的 Json 转模型的需求了。总结起来就是以下几点:

所有要转换的 model 继承 NSObject

使用类的类型来实例化对象

通过反射获得对象的所有属性名

通过 方法来给属性设置值

对于在最后提出的几个问题,我这里就不一一详细地说明了。大家可以点这里看看我的实现。大家可以使用 CocoaPods 或者 Carthage 将 ModelSwift 集成到项目中。如果在使用中有什么问题可以 issue 我,也可以给个 star 持续关注。

总结

以上是内存溢出为你收集整理的教你如何用Swift写个json转模型的开源库全部内容,希望文章能够帮你解决教你如何用Swift写个json转模型的开源库所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1064669.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-26
下一篇 2022-05-26

发表评论

登录后才能评论

评论列表(0条)

保存