【KakaJSON手冊】02_JSON轉Model_02_資料型別

M了個J發表於2019-08-15

由於JSON格式的能表達的資料型別是比較有限的,所以伺服器返回的JSON資料有時無法自動轉換成客戶端想要的資料型別。

  • 比如伺服器返回的時間可能是個毫秒數1565480696,但客戶端想要的是Date型別
  • 再比如客戶端想要的是浮點數Double型別,但伺服器返回的可能是字串型別"6.45"
  • 再比如客戶端想要的是Bool型別,但伺服器返回的可能是"true""1"0
  • 再比如客戶端想要的是高精度浮點數NSDecimalNumber型別,伺服器為了維持精度可能返回字串"0.1234567890123456789"
  • 上述不同型別之間的匹配轉換問題,KakaJSON已經內建了常用的解析功能,全部自動轉換,不用做任何額外的配置。本文會介紹一些常見的資料型別匹配、轉換功能。

Int

struct Student: Convertible {
    var age1: Int8 = 6
    var age2: Int16 = 0
    var age3: Int32 = 0
    var age4: Int64 = 0
    var age5: UInt8 = 0
    var age6: UInt16 = 0
    var age7: UInt32 = 0
    var age8: UInt64 = 0
    var age9: UInt = 0
    var age10: Int = 0
}

let json: [String: Any] = [
    "age1": "suan8fa8",
    "age2": "6suan8fa8",
    "age3": "6",
    "age4": 6.66,
    "age5": NSNumber(value: 6.66),
    "age6": Int32(6),
    "age7": true,
    "age8": "FALSE", 
    "age9": Decimal(6.66),
    "age10": NSDecimalNumber(value: 6.66)
]

let student = json.kk.model(Student.self)
// 解析失敗,所以使用預設值6
XCTAssert(student?.age1 == 6) 
// 解析到前面的數字6
XCTAssert(student?.age2 == 6) 
XCTAssert(student?.age3 == 6)
XCTAssert(student?.age4 == 6)
XCTAssert(student?.age5 == 6)
XCTAssert(student?.age6 == 6)
// true為1,false為0
XCTAssert(student?.age7 == 1) 
// 字串true\TRUE\YES\yes都為1,字串false\FALSE\NO\no都為0
XCTAssert(student?.age8 == 0) 
XCTAssert(student?.age9 == 6)
XCTAssert(student?.age10 == 6)

Float

struct Student: Convertible {
    var height1: Float = 0.0
    var height2: Float = 0.0
    var height3: Float = 0.0
    var height4: Float = 0.0
    var height5: Float = 0.0
    var height6: Float = 0.0
    var height7: Float = 0.0
}

let longFloatString = "0.12345678"
let longFloat: Float = 0.12345678

let json: [String: Any] = [
    "height1": "6.66suan8fa8",
    "height2": longFloatString,
    "height3": NSDecimalNumber(string: longFloatString),
    "height4": Decimal(string: longFloatString) as Any,
    "height5": 666,
    "height6": true,
    "height7": "NO"
]

let student = json.kk.model(Student.self)
XCTAssert(student?.height1 == 6.66)
XCTAssert(student?.height2 == longFloat)
XCTAssert(student?.height3 == longFloat)
XCTAssert(student?.height4 == longFloat)
XCTAssert(student?.height5 == 666.0)
// true為1.0,false為0.0
XCTAssert(student?.height6 == 1.0)
// 字串true\TRUE\YES\yes都為1.0,字串false\FALSE\NO\no都為0.0
XCTAssert(student?.height7 == 0.0)

Double

struct Student: Convertible {
    var height1: Double = 0.0
    var height2: Double = 0.0
    var height3: Double = 0.0
    var height4: Double = 0.0
    var height5: Double = 0.0
    var height6: Double = 0.0
    var height7: Double = 0.0
}

let longDoubleString = "0.1234567890123456"
let longDouble: Double = 0.1234567890123456

let json: [String: Any] = [
    "height1": "6.66suan8fa8",
    "height2": longDoubleString,
    "height3": NSDecimalNumber(string: longDoubleString),
    "height4": Decimal(string: longDoubleString) as Any,
    "height5": 666,
    "height6": true,
    "height7": "NO"
]

let student = json.kk.model(Student.self)
XCTAssert(student?.height1 == 6.66)
XCTAssert(student?.height2 == longDouble)
XCTAssert(student?.height3 == longDouble)
XCTAssert(student?.height4 == longDouble)
XCTAssert(student?.height5 == 666.0)
// true為1.0,false為0.0
XCTAssert(student?.height6 == 1.0)
// 字串true\TRUE\YES\yes都為1.0,字串false\FALSE\NO\no都為0.0
XCTAssert(student?.height7 == 0.0)

Bool

struct Student: Convertible {
    var rich1: Bool = false
    var rich2: Bool = false
    var rich3: Bool = false
    var rich4: Bool = false
    var rich5: Bool = false
    var rich6: Bool = false
}

let json: [String: Any] = [
    "rich1": 100,
    "rich2": 0.0,
    "rich3": "1",
    "rich4": NSNumber(value: 0.666),
    "rich5": "true",
    "rich6": "NO" 
]

let student = json.kk.model(Student.self)
// 數值0為false,數值非0都是true
XCTAssert(student?.rich1 == true)
XCTAssert(student?.rich2 == false)
XCTAssert(student?.rich3 == true)
// 0.666是非0,所以為true
XCTAssert(student?.rich4 == true)
// 字串true\TRUE\YES\yes都為true
XCTAssert(student?.rich5 == true)
// 字串false\FALSE\NO\no都為false
XCTAssert(student?.rich6 == false)

String

// 不管是String、NSString,還是NSMutableString,都是等效支援的
struct Student: Convertible {
    var name1: String = ""
    var name2: String = ""
    var name3: NSString = ""
    var name4: NSString = ""
    var name5: NSMutableString = ""
    var name6: NSMutableString = ""
    var name7: String = ""
    var name8: String = ""
}

let longDecimalString = "0.123456789012345678901234567890123456789"
var longDecimalNumber = NSDecimalNumber(string: longDecimalString)

let json: [String: Any] = [
    "name1": 666,
    "name2": NSMutableString(string: "777"),
    "name3": [1,[2,3],"4"],
    "name4": longDecimalNumber,
    "name5": 6.66,
    "name6": false,
    "name7": NSURL(fileURLWithPath: "/users/mj/desktop"),
    "name8": URL(string: "http://www.520suanfa.com") as Any
]

let student = json.kk.model(Student.self)
XCTAssert(student?.name1 == "666")
XCTAssert(student?.name2 == "777")
// 這裡相當於預設呼叫了陣列的description方法
XCTAssert(student?.name3 == "[1, [2, 3], \"4\"]")
XCTAssert(student?.name4 == longDecimalString as NSString)
XCTAssert(student?.name5 == "6.66")
XCTAssert(student?.name6 == "false")
XCTAssert(student?.name7 == "file:///users/mj/desktop")
XCTAssert(student?.name8 == "http://www.520suanfa.com")

Decimal

struct Student: Convertible {
    var money1: Decimal = 0
    var money2: Decimal = 0
    var money3: Decimal = 0
    var money4: Decimal = 0
    var money5: Decimal = 0
    var money6: Decimal = 0
}

let longDoubleString = "0.1234567890123456"
let longDouble: Double = 0.1234567890123456
let longDecimalString = "0.123456789012345678901234567890123456789"
var longDecimalNumber = NSDecimalNumber(string: longDecimalString)

let json: [String: Any] = [
    "money1": longDouble,
    "money2": true,
    "money3": longDecimalNumber,
    "money4": longDecimalString,
    "money5": 666,
    "money6": "NO"
]

let student = json.kk.model(Student.self)
XCTAssert(student?.money1 == Decimal(string: longDoubleString))
XCTAssert(student?.money2 == 1)
XCTAssert(student?.money3 == Decimal(string: longDecimalString))
XCTAssert(student?.money4 == Decimal(string: longDecimalString))
XCTAssert(student?.money5 == 666)
XCTAssert(student?.money6 == 0)

NSDecimalNumber

struct Student: Convertible {
    var money1: NSDecimalNumber = 0
    var money2: NSDecimalNumber = 0
    var money3: NSDecimalNumber = 0
    var money4: NSDecimalNumber = 0
    var money5: NSDecimalNumber = 0
    var money6: NSDecimalNumber = 0
}

let longDoubleString = "0.1234567890123456"
let longDouble: Double = 0.1234567890123456
let longDecimalString = "0.123456789012345678901234567890123456789"
var longDecimal = Decimal(string: longDecimalString)!
var longDecimalNumber = NSDecimalNumber(string: longDecimalString)

let json: [String: Any] = [
    "money1": longDouble,
    "money2": true,
    "money3": longDecimal,
    "money4": longDecimalString,
    "money5": 666.0,
    "money6": "NO"
]

let student = json.kk.model(Student.self)
XCTAssert(student?.money1 == NSDecimalNumber(string: longDoubleString))
XCTAssert(student?.money2 == true)
XCTAssert(student?.money2 == 1)
XCTAssert(student?.money3 == longDecimalNumber)
XCTAssert(student?.money4 == longDecimalNumber)
XCTAssert(student?.money5 == 666)
XCTAssert(student?.money6 == false)
XCTAssert(student?.money6 == 0)

NSNumber

struct Student: Convertible {
    var money1: NSNumber = 0
    var money2: NSNumber = 0
    var money3: NSNumber = 0
    var money4: NSNumber = 0
    var money5: NSNumber = 0
    var money6: NSNumber = 0
}

let longDoubleString = "0.1234567890123456"
let longDouble: Double = 0.1234567890123456

let json: [String: Any] = [
    "money1": longDouble,
    "money2": true,
    "money3": Decimal(string: longDoubleString) as Any,
    "money4": longDoubleString,
    "money5": 666.0,
    "money6": "NO"
]

let student = json.kk.model(Student.self)
XCTAssert(student?.money1 == NSNumber(value: longDouble))
XCTAssert(student?.money2 == true)
XCTAssert(student?.money2 == 1)
XCTAssert(student?.money2 == 1.0)
XCTAssert(student?.money3 == NSNumber(value: longDouble))
XCTAssert(student?.money4 == NSNumber(value: longDouble))
XCTAssert(student?.money5 == 666)
XCTAssert(student?.money5 == 666.0)
XCTAssert(student?.money6 == false)
XCTAssert(student?.money6 == 0)
XCTAssert(student?.money6 == 0.0)

Optional

// ?再多都不是問題
struct Student: Convertible {
    var rich1: Bool = false
    var rich2: Bool? = false
    var rich3: Bool?? = false
    var rich4: Bool??? = false
    var rich5: Bool???? = false
    var rich6: Bool????? = false
}

let rich1: Int????? = 100
let rich2: Double???? = 0.0
let rich3: String??? = "0"
let rich4: NSNumber?? = NSNumber(value: 0.666)
let rich5: String? = "true"
let rich6: String = "NO" 

let json: [String: Any] = [
    "rich1": rich1 as Any,
    "rich2": rich2 as Any,
    "rich3": rich3 as Any,
    "rich4": rich4 as Any,
    "rich5": rich5 as Any,
    "rich6": rich6
]

let student = json.kk.model(Student.self)
XCTAssert(student?.rich1 == true)
XCTAssert(student?.rich2 == false)
XCTAssert(student?.rich3 == false)
XCTAssert(student?.rich4 == true)
XCTAssert(student?.rich5 == true)
XCTAssert(student?.rich6 == false)

URL

// 支援URL和NSURL
struct Student: Convertible {
    var url1: NSURL?
    var url2: NSURL?
    var url3: URL?
    var url4: URL?
}

let url = "http://520suanfa.com/紅黑樹"
let encodedUrl = "http://520suanfa.com/%E7%BA%A2%E9%BB%91%E6%A0%91"

let json: [String: Any] = [
    "url1": url,
    "url2": URL(string: encodedUrl) as Any,
    "url3": url,
    "url4": NSURL(string: encodedUrl) as Any
]

let student = json.kk.model(Student.self)
XCTAssert(student?.url1?.absoluteString == encodedUrl)
XCTAssert(student?.url2?.absoluteString == encodedUrl)
XCTAssert(student?.url3?.absoluteString == encodedUrl)
XCTAssert(student?.url4?.absoluteString == encodedUrl)

Data

// 支援NSData和Data
struct Student: Convertible {
    var data1: NSData?
    var data2: NSData?
    var data3: Data?
    var data4: Data?
}

let utf8 = String.Encoding.utf8
let str = "RedBlackTree"
let data = str.data(using: utf8)!

let json: [String: Any] = [
    "data1": str,
    "data2": data,
    "data3": str,
    "data4": NSData(data: data)
]

let student = json.kk.model(Student.self)
XCTAssert(String(data: (student?.data1)! as Data, encoding: utf8) == str)
XCTAssert(String(data: (student?.data2)! as Data, encoding: utf8) == str)
XCTAssert(String(data: (student?.data3)!, encoding: utf8) == str)
XCTAssert(String(data: (student?.data4)!, encoding: utf8) == str)

Enum

// 遵守一下ConvertibleEnum協議即可支援帶有RawValue的Enum型別
enum Grade: String, ConvertibleEnum {
    case perfect = "A"
    case great = "B"
    case good = "C"
    case bad = "D"
}

struct Student: Convertible {
    var grade1: Grade = .perfect
    var grade2: Grade = .perfect
}

let json: [String: Any] = [
    "grade1": "C",
    "grade2": "D"
]

let student = json.kk.model(Student.self)
XCTAssert(student?.grade1 == .good)
XCTAssert(student?.grade2 == .bad)

// 換個浮點數試試
enum Grade2: Double, ConvertibleEnum {
    case perfect = 8.88
    case great = 7.77
    case good = 6.66
    case bad = 5.55
}

struct Student2: Convertible {
    var grade1: Grade2 = .perfect
    var grade2: Grade2 = .perfect
    var grade3: Grade2 = .perfect
    var grade4: Grade2 = .perfect
}

let json2: [String: Any] = [
    "grade1": "5.55kaka",
    "grade2": 6.66,
    "grade3": NSNumber(value: 7.77),
    "grade4": NSDecimalNumber(string: "8.88")
]

let student2 = json2.kk.model(Student2.self)
XCTAssert(student2?.grade1 == .bad)
XCTAssert(student2?.grade2 == .good)
XCTAssert(student2?.grade3 == .great)
XCTAssert(student2?.grade4 == .perfect)

相關文章