寫在前面的話
現在很多iOS專案的開發開始轉向Swift語言。 相信 Swift語言很快會成為iOS工程師 必備技能。 字典轉模型, 模型轉轉字典在開發過程中扮演非常重要的角色。 今天就和大家分享一下使用Swift,如何進行字典模型互轉。 **
Demo在這裡
為了讓工作做到極致,這裡先提供一個工具 JSONExport。該工具能夠使用json資料生成對應的模型類檔案,支援oc和Swift,非常不錯。
功能:
1,字典–>模型 :最簡單的形式
class User: NSObject { //模型類
var name:String?
var icon:String?
// print時會呼叫。相當於java中的 toString()。為了程式碼整潔下面的模型去了這個計算屬性。測試時請下載demo
override internal var description: String {
return "name: (name)
icon:(icon)
"
}
}
func func1(){
let dict = ["name":"Jack","icon":"lufy.png"]
if let user = User.objectWithKeyValues(dict) as? User{
print("(user)")
}
}
輸出: name: Optional("Jack")
icon: Optional("lufy.png")
複製程式碼
2,字典–>模型 :模型中包裹模型
//模型類
class Status :NSObject {
var text:String?
var user:User? //與 1 中的模型相同
var retweetedStatus:Status?
}
func func2(){
let dict = ["text":"Agree!Nice weather!",
"user":["name":"Jack","icon":"lufy.png"],
"retweetedStatus":["text":"Nice weather!",
"user":["name":"Rose","icon":"nami.png"]]
]
if let status = Status.objectWithKeyValues(dict) as? Status{
print("(status)")
}
}
輸出:
text:Optional("Agree!Nice weather!")
user:Optional(name: Optional("Jack") icon:Optional("lufy.png"))
retweetedStatus:Optional(text:Optional("Nice weather!")
user:Optional(name: Optional("Rose")icon:Optional("nami.png"))
retweetedStatus:nil)
複製程式碼
3,字典–>模型: 字典中包裹陣列, 陣列中的元素是 一個模型對應的字典
//模型類, 必須遵守DictModelProtocol協議, 並實現customClassMapping方法。
class UserGroup: NSObject,DictModelProtocol {
var groupName:String?; //團隊名稱
var numbers:NSArray? //成員,儲存User例項
static func customClassMapping() -> [String: String]?{
return ["numbers":"User"]; //指定numbers陣列中的元素型別是User
}
}
func func3(){
let dict = ["groupName":"Dream Team",
"numbers":[["name":"Jack","icon":"lufy.png"],
["name":"Rose","icon":"nami.png"]]
]
if let group = UserGroup.objectWithKeyValues(dict){
print("(group)")
}
}
輸出: groupName:Optional("Dream Team")
numbers:Optional((
"name: Optional("Jack")
icon:Optional("lufy.png")
",
"name: Optional("Rose")
icon:Optional("nami.png")
"
))
複製程式碼
4,字典–>模型: 將一個字典陣列轉成模型陣列
func func4(){
let arrayOfStatus = [["text":"Agree!Nice weather!",
"user":["name":"Jack",
"icon":"lufy.png"
],
"retweetedStatus":["text":"Nice weather!",
"user":["name":"Rose",
"icon":"nami.png"
]
]
],
["text":"2___Agree!Nice weather!",
"user":["name":"2___Jack",
"icon":"2___lufy.png"
],
"retweetedStatus":["text":"2___Nice weather!",
"user":["name":"2___Rose",
"icon":"2___nami.png"
]
]
]]
if let status = Status.objectArrayWithKeyValuesArray(arrayOfStatus){
for item in status{ //列印出陣列的元素
print(item)
}
}
}
輸出:
text:Optional("Agree!Nice weather!")
user:Optional(name: Optional("Jack")icon:Optional("lufy.png"))
retweetedStatus:Optional(text:Optional("Nice weather!")
user:Optional(name: Optional("Rose") icon:Optional("nami.png"))
retweetedStatus:nil
)
text:Optional("2___Agree!Nice weather!")
user:Optional(name: Optional("2___Jack")icon:Optional("2___lufy.png"))
retweetedStatus:Optional(text:Optional("2___Nice weather!")
user:Optional(name: Optional("2___Rose")icon:Optional("2___nami.png"))
retweetedStatus:nil
)
複製程式碼
5 模型–>字典: 最簡單形式
func func5(){
let user = User()
user.name = "hejunm"
user.icon = "my.png"
if let dict = user.keyValues{
do{ //轉化為JSON 字串,列印出來更直觀
let data = try NSJSONSerialization.dataWithJSONObject(dict, options: .PrettyPrinted)
print(NSString(data: data, encoding: NSUTF8StringEncoding))
}catch{}
}
}
輸出:
Optional({
"icon" : "my.png",
"name" : "hejunm"
})
複製程式碼
6 模型–>字典: 模型中還有模型
func func6(){
let user = User()
user.name = "retweeted user hejunm"
user.icon = "my.png"
let retweetedStatus = Status(); //轉發微博
retweetedStatus.text = "this is retweeted status";
retweetedStatus.user = user
let oriUser = User()
oriUser.name = "original user"
oriUser.icon = "my.png"
let oriStatus = Status(); //原微博
oriStatus.text = "this is original status"
oriStatus.user = oriUser
oriStatus.retweetedStatus = retweetedStatus
let dict = oriStatus.keyValues
do{ //轉化為JSON 字串
var data = try NSJSONSerialization.dataWithJSONObject(dict!, options: .PrettyPrinted)
print(NSString(data: data, encoding: NSUTF8StringEncoding))
}catch{
}
}
輸出:
Optional({
"text" : "this is original status",
"user" : {
"icon" : "my.png",
"name" : "original user"
},
"retweetedStatus" : {
"text" : "this is retweeted status",
"user" : {
"icon" : "my.png",
"name" : "retweeted user hejunm"
}
}
})
複製程式碼
7,模型–>字典 : 模型陣列轉字典陣列
func func7(){
let user1 = User()
user1.name = "hejunm_1"
user1.icon = "my.png_1"
let user2 = User()
user2.name = "hejunm_2"
user2.icon = "my.png_2"
let userArray = [user1,user2] as NSArray
if let dicts = userArray.keyValuesArray{
do{
let data = try NSJSONSerialization.dataWithJSONObject(dicts, options: .PrettyPrinted) //轉成json字串
print(NSString(data: data, encoding: NSUTF8StringEncoding))
}catch{
}
}
}
輸出:
Optional([
{
"icon" : "my.png_1",
"name" : "hejunm_1"
},
{
"icon" : "my.png_2",
"name" : "hejunm_2"
}
])
複製程式碼
原始碼
字典–>模型
//
// HE_Dict2Model.swift
// HEExtention
//
// Created by 賀俊孟 on 16/4/27.
// Copyright © 2016年 賀俊孟. All rights reserved.
// 字典傳模型
import Foundation
/** 當字典中存在陣列, 並且陣列中儲存的值得型別是字典, 那麼就需要指定陣列中的字典對應的類型別。
這裡以鍵值對的形式儲存
eg 字典如下:
key: [[key1:value1, key2:value2],[key1:value3, key2:value4],[key1:value5, key2:value6]]
key: key值
value: 字典[key1:value1, key2:value2] 對應的模型
*/
@objc public protocol DictModelProtocol{
static func customClassMapping() -> [String: String]?
}
extension NSObject{
//dict: 要進行轉換的字典
class func objectWithKeyValues(dict: NSDictionary)->AnyObject?{
if HEFoundation.isClassFromFoundation(self) {
print("只有自定義模型類才可以字典轉模型")
assert(true)
return nil
}
let obj:AnyObject = self.init()
var cls:AnyClass = self.classForCoder() //當前類的型別
while("NSObject" != "(cls)"){
var count:UInt32 = 0
let properties = class_copyPropertyList(cls, &count) //獲取屬性列表
for i in 0..<count{
let property = properties[Int(i)] //獲取模型中的某一個屬性
let propertyType = String.fromCString(property_getAttributes(property))! //屬性型別
let propertyKey = String.fromCString(property_getName(property))! //屬性名稱
if propertyKey == "description"{ continue } //description是Foundation中的計算型屬性,是例項的描述資訊
var value:AnyObject! = dict[propertyKey] //取得字典中的值
if value == nil {continue}
let valueType = "(value.classForCoder)" //字典中儲存的值得型別
if valueType == "NSDictionary"{ //1,值是字典。 這個字典要對應一個自定義的模型類並且這個類不是Foundation中定義的型別。
let subModelStr:String! = HEFoundation.getType(propertyType)
if subModelStr == nil{
print("你定義的模型與字典不匹配。 字典中的鍵(propertyKey) 對應一個自定義的 模型")
assert(true)
}
if let subModelClass = NSClassFromString(subModelStr){
value = subModelClass.objectWithKeyValues(value as! NSDictionary) //遞迴
}
}else if valueType == "NSArray"{ //值是陣列。 陣列中存放字典。 將字典轉換成模型。 如果協議中沒有定義對映關係,就不做處理
if self.respondsToSelector("customClassMapping") {
if var subModelClassName = cls.customClassMapping()?[propertyKey]{ //子模型的類名稱
subModelClassName = HEFoundation.bundlePath+"."+subModelClassName
if let subModelClass = NSClassFromString(subModelClassName){
value = subModelClass.objectArrayWithKeyValuesArray(value as! NSArray);
}
}
}
}
obj.setValue(value, forKey: propertyKey)
}
free(properties) //釋放記憶體
cls = cls.superclass()! //處理父類
}
return obj
}
/**
將字典陣列轉換成模型陣列
array: 要轉換的陣列, 陣列中包含的字典所對應的模型類就是 呼叫這個類方法的類
當陣列中巢狀陣列, 內部的陣列包含字典,cls就是內部陣列中的字典對應的模型
*/
class func objectArrayWithKeyValuesArray(array: NSArray)->NSArray?{
if array.count == 0{
return nil
}
var result = [AnyObject]()
for item in array{
let type = "(item.classForCoder)"
if type == "NSDictionary"{
if let model = objectWithKeyValues(item as! NSDictionary){
result.append(model)
}
}else if type == "NSArray"{
if let model = objectArrayWithKeyValuesArray(item as! NSArray){
result.append(model)
}
}else{
result.append(item)
}
}
if result.count==0{
return nil
}else{
return result
}
}
}
複製程式碼
模型–>字典
//
// HE_Model2Dict.swift
// HEExtention
//
// Created by 賀俊孟 on 16/4/27.
// Copyright © 2016年 賀俊孟. All rights reserved.
// 模型傳字典
import Foundation
extension NSObject{
var keyValues:[String:AnyObject]?{ //獲取一個模型對應的字典
get{
var result = [String: AnyObject]() //儲存結果
var classType:AnyClass = self.classForCoder
while("NSObject" != "(classType)" ){
var count:UInt32 = 0
let properties = class_copyPropertyList(classType, &count)
for i in 0..<count{
let property = properties[Int(i)]
let propertyKey = String.fromCString(property_getName(property))! //模型中屬性名稱
let propertyType = String.fromCString(property_getAttributes(property))! //模型中屬性型別
if "description" == propertyKey{ continue } //描述,不是屬性
let tempValue:AnyObject! = self.valueForKey(propertyKey)
if tempValue == nil { continue }
if let _ = HEFoundation.getType(propertyType) { //1,自定義的類
result[propertyKey] = tempValue.keyValues
}else if (propertyType.containsString("NSArray")){ //2, 陣列, 將陣列中的模型轉成字典
result[propertyKey] = tempValue.keyValuesArray //3, 基本資料
}else{
result[propertyKey] = tempValue
}
}
free(properties)
classType = classType.superclass()!
}
if result.count == 0{
return nil
}else{
return result
}
}
}
}
extension NSArray{ //陣列的擴充
var keyValuesArray:[AnyObject]?{
get{
var result = [AnyObject]()
for item in self{
if !HEFoundation.isClassFromFoundation(item.classForCoder){ //1,自定義的類
let subKeyValues:[String:AnyObject]! = item.keyValues
if subKeyValues == nil {continue}
result.append(subKeyValues)
}else if item.classForCoder == NSArray.classForCoder(){ //2, 如果item 是陣列
let subKeyValues:[AnyObject]! = item.keyValuesArray
if subKeyValues == nil {continue}
result.append(subKeyValues)
}else{ //3, 基本資料型別
result.append(item)
}
}
if result.count == 0{
return nil
}else{
return result
}
}
}
}
複製程式碼
輔助類
//
// HEFoundation.swift
// HEExtention
//
// Created by 賀俊孟 on 16/4/27.
// Copyright © 2016年 賀俊孟. All rights reserved.
import Foundation
class HEFoundation {
static let set = NSSet(array: [
NSURL.classForCoder(),
NSDate.classForCoder(),
NSValue.classForCoder(),
NSData.classForCoder(),
NSError.classForCoder(),
NSArray.classForCoder(),
NSDictionary.classForCoder(),
NSString.classForCoder(),
NSAttributedString.classForCoder()
])
static let bundlePath = NSBundle.mainBundle().infoDictionary!["CFBundleExecutable"] as! String
/*** 判斷某個類是否是 Foundation中自帶的類 */
class func isClassFromFoundation(c:AnyClass)->Bool {
var result = false
if c == NSObject.classForCoder(){
result = true
}else{
set.enumerateObjectsUsingBlock({ (foundation, stop) -> Void in
if c.isSubclassOfClass(foundation as! AnyClass) {
result = true
stop.initialize(true)
}
})
}
return result
}
/** 很據屬性資訊, 獲得自定義類的 類名*/
/**
let propertyType = String.fromCString(property_getAttributes(property))! 獲取屬性型別
到這個屬性的型別是自定義的類時, 會得到下面的格式: T+@+"+..+工程的名字+數字+類名+"+,+其他,
而我們想要的只是類名,所以要修改這個字串
*/
class func getType(var code:String)->String?{
if !code.containsString(bundlePath){ //不是自定義類
return nil
}
code = code.componentsSeparatedByString(""")[1]
if let range = code.rangeOfString(bundlePath){
code = code.substringFromIndex(range.endIndex)
var numStr = "" //類名前面的數字
for c:Character in code.characters{
if c <= "9" && c >= "0"{
numStr+=String(c)
}
}
if let numRange = code.rangeOfString(numStr){
code = code.substringFromIndex(numRange.endIndex)
}
return bundlePath+"."+code
}
return nil
}
}
複製程式碼