NamadaJSON
NamadaJSON 是一个框架,用于简化将 JSON 转换为 Swift 对象(类或结构体)或将对象转换为 JSON 的操作。
使用 NamadaJSON,您无需在对象上实现映射函数或继承 NSObject。
NamadaJSON 使用属性包装和反射来实现双向映射。
需求
安装
NamadaJSON 通过 CocoaPods 提供。要安装它,只需将以下行添加到 Podfile 中
pod 'NamadaJSON'
作者
Nayanda Haberty,[email protected]
许可证
NamadaJSON 在 MIT 许可下可用。有关更多信息,请参阅 LICENSE 文件。
使用方法
基本使用
例如,如果您需要将 User JSON 映射到 Swift 对象,只需创建 User 类/结构体并实现 JSONAble 协议
,然后使用 @AutoMapping 属性
标记所有需要映射到 JSON 的属性。
class User: JSONAble {
@AutoMapping
var name: String = ""
@AutoMapping
var userName: String = ""
@AutoMapping
var age: Int = 0
required init() {}
}
然后您可以像这样将 JSON 解析为 User Swift 对象或反之亦然
let user: User = getUserFromSomewhere()
//to JSON
let jsonObject: [String: Any] = user.toJSON()
let jsonString: String = user.toJSONString()
let prettyJson: String = user.toPrettyPrintedJSON()
//from JSON
let userFromJSON: User? = try? .parse(from: jsonObject)
let userFromString: User? = try? .parse(fromString: jsonString)
JSONAble 支持继承,只要基类具有 @AutoMapping 或 @ManualMapping 属性
且实现 JSONAble
class User: JSONAble {
@AutoMapping
var name: String = ""
@AutoMapping
var userName: String = ""
@AutoMapping
var age: Int = 0
required init() {}
}
class Admin: User {
@AutoMapping
var role: String = ""
}
如果 JSON 的属性名与 Swift 对象中的属性不同,则可以在属性中传递 JSON 属性的名称
class User: JSONAble {
@AutoMapping
var name: String = ""
// pass the name of the JSON property
@AutoMapping(key: "user_name")
var userName: String = ""
@AutoMapping
var age: Int = 0
required init() {}
}
支持的属性数据类型包括:字符串、所有类型的整数、Double、Float、Bool、实现 JSONAble 或 JSONParseable 的任何对象,以及任何 JSONParseable 类型的数组 所有属性都可以是可选的。
class User: JSONAble {
@AutoMapping
var name: String?
// pass the name of the JSON property
@AutoMapping(key: "user_name")
var userName: String = ""
@AutoMapping
var age: Int = 0
@AutoMapping
var hobbies: [String]?
required init() {}
}
手动映射
如果您使用不支持的数据类型作为对象属性,您有两个选项
- 使用
JSONAbleMapper 对象
。提供的JSONAbleMapper
是StringDateMapper
和LongDateMapper
。StringDateMapper 接受您自定义模式的字符串并将其转换为日期或反之亦然。LongDateMapper 接受以UNIX 长日期格式
表示的数字并将它转换为日期或反之亦然。然后您需要将映射器传递给@ManualMapping 属性属性
class User: JSONAble {
@AutoMapping
var name: String?
@AutoMapping(key: "user_name")
var userName: String = ""
@AutoMapping
var age: Int = 0
@AutoMapping
var hobbies: [String]?
// manual mapping
@ManualMapping(mapper: StringDateMapper(pattern: "dd-MM-yyyy"))
var birthDate: Date = .init()
@ManualMapping(mapper: LongDateMapper())
var registrationDate: Date = .init()
required init() {}
}
如果您的数据类型是可选的、数组或两者兼而有之,您可以使用 forOptional
计算属性、forArray
计算属性或两者的组合,因为该属性是 JSONAbleMapper 协议的扩展。属性调用的顺序会影响 JSONAbleMapper 类型的结果。
class User: JSONAble {
@AutoMapping
var name: String?
@AutoMapping(key: "user_name")
var userName: String = ""
@AutoMapping
var age: Int = 0
@AutoMapping
var hobbies: [String]?
// optional
@ManualMapping(mapper: StringDateMapper(pattern: "dd-MM-yyyy").forOptional)
var birthDate: Date? = nil
// array
@ManualMapping(mapper: LongDateMapper().forArray)
var loginTimes: [Date] = []
// array optional
@ManualMapping(mapper: LongDateMapper().forArray.forOptional)
var crashesTimes: [Date]? = nil
// array of optional
@ManualMapping(mapper: LongDateMapper().forOptional.forArray)
var someTimes: [Date?] = []
required init() {}
}
如果您想实现自己的映射器,您可以创建自己的 Mapper 类/结构体,该结构体实现了 JSONAbleMapper 协议
,并像之前一样在使用 ManualMapping 属性属性
时使用它
open class MyOwnDateMapper: JSONAbleMapper {
public typealias MappedObject = Date
public typealias Parseable = Double
public init() { }
open func from(jsonCompatible: JSONCompatible) throws -> Date {
guard let long = jsonCompatible.asNumberCompatible?.doubleValue else {
throw JSONParseableError(description: "Cannot parse date from non number")
}
return .init(timeIntervalSince1970: .init(integerLiteral: Int64(long)))
}
open func toJSONParseable(_ object: Date) -> Double {
return Double(object.timeIntervalSince1970)
}
open func toJSONString(_ object: Date) -> String {
return String(Double(object.timeIntervalSince1970))
}
}
相关联的类型 Parseable
限制为任何 JSONParseable,这些是 字符串、所有类型的整数、Double、Float、Bool、实现 JSONAble 或 JSONParseable 的任何对象,以及任何 JSONParseable 类型的数组。关联类型 MappedObject
是映射到 JSON 的数据类型,JSONCompatible 协议
实现 Swift 中原生可接受的 JSON 类型
,这些是:NSString、String、NSNumber、Bool、NSDictionary、Dictionary
- 另一个选项是将 `JSONParseable` 实现到您希望的数据类型中。这种方法唯一的缺点是与另一种方法相比,更难创建多个解析方法。这种方法的优点是您可以像标准方法一样使用 `
AutoMapping property attribute
`。
extension Date: JSONParseable {
public func toJSONCompatible() -> JSONCompatible {
return toJSONString()
}
public func toJSONString() -> String {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
return formatter.string(from: self).toJSONString()
}
public static func parse(fromJSONCompatible compatible: JSONCompatible) throws -> JSONParseable {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
guard let string = compatible as? String, let date = formatter.date(from: myString) else {
throw JSONParseableError(description: "Cannot parse from any type different than RawValue")
}
return date
}
public static func parse(fromJSONString string: String) throws -> JSONParseable {
return try parse(fromJSONCompatible: String.parse(fromJSONString: string))
}
}
函数 `toJSONCompatible() -> JSONCompatible
` 和 `static func parse(fromJSONCompatible compatible: JSONCompatible) throws -> JSONParseable
` 将用作从原始数据类型到原生可接受 JSON 数据类型或反之亦然的解析器。函数 `func toJSONString() -> String
` 和 `static func parse(fromJSONString string: String) throws -> JSONParseable
` 将用作将字符串 JSON 转换为 Swift 数据类型或反之亦然的解析器。
然后您就可以像支持原生那样使用数据类型了。
class User: JSONAble {
@AutoMapping
var name: String?
@AutoMapping(key: "user_name")
var userName: String = ""
@AutoMapping
var age: Int = 0
@AutoMapping
var hobbies: [String]?
// date now supported
@AutoMapping(key: "date_of_birth")
var dateOfBirth: Date?
required init() {}
}
需要考虑的一点是,JSON 字符串含有引号,所以如果您的映射器正在映射到和从 String,别忘了在返回值中添加引号,或者调用 .toJSONString()
函数。
open class StringDateMapper: JSONAbleMapper {
public typealias MappedObject = Date
public typealias Parseable = String
private lazy var dateFormatter: DateFormatter = .init()
public init(pattern: String) {
dateFormatter.dateFormat = pattern
}
open func from(jsonCompatible: JSONCompatible) throws -> Date {
guard let string = jsonCompatible.asStringCompatible else {
throw JSONParseableError(description: "Cannot parse date from non string")
}
guard let date = dateFormatter.date(from: string) else {
throw JSONParseableError(description: "Failed to parse string to Date")
}
return date
}
open func toJSONParseable(_ object: Date) -> String {
return dateFormatter.string(from: object)
}
// don't forger to add toJSONString(), so it will added quote to the return value
open func toJSONString(_ object: Date) -> String {
return dateFormatter.string(from: object).toJSONString()
}
}
带有原始表示(如 String、Int 或任何可解析 JSON 的枚举)的类型需要实现 `JSONParseable 协议
`,由于实现已在 `RawRepresentable 扩展` 中实现,所以实现可以为零。
enum UserType: String, JSONParseable {
case regular
case premium
case blacklisted
}
然后您就可以像通常一样将这种数据类型用作 JSON 属性。
class User: JSONAble {
@AutoMapping
var type: UserType = .regular
@AutoMapping
var name: String?
@AutoMapping(key: "user_name")
var userName: String = ""
@AutoMapping
var age: Int = 0
@AutoMapping
var hobbies: [String]?
@AutoMapping(key: "date_of_birth")
var dateOfBirth: Date?
required init() {}
}
额外内容
实现了 `JSONAble 协议` 的任何对象都可以像字典一样处理。
let user = User()
user["user_name"] = "this is username"
// will print "this is username"
print(user.userName)
let userName: String = user["user_name"] ?? ""
// will print "this is username"
print(userName)