NamadaJSON 1.0.2

NamadaJSON 1.0.2

nayanda1 维护。



  • 作者
  • nayanda

NamadaJSON

NamadaJSON 是一个框架,用于简化将 JSON 转换为 Swift 对象(类或结构体)或将对象转换为 JSON 的操作。

使用 NamadaJSON,您无需在对象上实现映射函数或继承 NSObject。

NamadaJSON 使用属性包装和反射来实现双向映射。

CI Status Version License Platform

需求

安装

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 对象。提供的 JSONAbleMapperStringDateMapperLongDateMapper。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、NSArray、Array 和 NSNull 将用作实际的 JSON 数据类型。请记住,可选类型不是 JSON-Compatible 的,请使用 NSNull 来表示 null。

  • 另一个选项是将 `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)