一、问题
- 对于使用
HandyJson或Codable来解析的数据转成Realm实例时失败!!! - 转成
Realm实例时HandyJson自定义映射无效!!!
二、解决方案
1. HandyJson
问题1:
let JsonString = """
{"genres":[{"id":28,"name":"Action"},{"id":12,"name":"Adventure"},{"id":16,"name":"Animation"},{"id":35,"name":"Comedy"},{"id":80,"name":"Crime"},{"id":99,"name":"Documentary"}]}
"""
class Genres: Object, HandyJSON {
@objc dynamic var id: String = ""
@objc dynamic var name: String?
override class func primaryKey() -> String? {
"id"
}
override required init() {
super.init()
}
}
let list: [Genres] = ([Genres].deserialize(from: JsonString, designatedPath: "genres") ?? []).compactMap { $0 }
list.forEach { print($0.id, $0.name) }
这里Realm对象的属性不可使用@Persisted标注,应该使用@objc dynamic标注为OC的动态类型。使用HandyJson有个便捷之处是解析时可以直接将id从Int转成String。
问题2:
这里有两种解决思路,直接上代码:
let JsonString = """
{"genres":[{"id":28,"name":"Action"},{"id":12,"name":"Adventure"},{"id":16,"name":"Animation"},{"id":35,"title":"Comedy"},{"id":80,"title":"Crime"},{"id":99,"title":"Documentary"}]}
"""
class Genres: Object, HandyJSON {
@objc dynamic var id: String = ""
@objc dynamic var name: String?
private var title: String?
override class func primaryKey() -> String? {
"id"
}
override required init() {
super.init()
}
/// 转换完成后赋值
func didFinishMapping() {
name = [name, title].compactMap { $0 }.first
}
}
let list: [Genres] = ([Genres].deserialize(from: JsonString, designatedPath: "genres") ?? []).compactMap { $0 }
list.forEach { print($0.id, $0.name) }
这种方式是在HandyJson的didFinishMapping中直接给属性赋值。再看另一种方式:
let JsonString = """
{"genres":[{"id":28,"name":"Action"},{"id":12,"name":"Adventure"},{"id":16,"name":"Animation"},{"id":35,"title":"Comedy"},{"id":80,"title":"Crime"},{"id":99,"title":"Documentary"}]}
"""
class Genres: Object, HandyJSON {
@objc dynamic var id: String = ""
@objc dynamic var name: String?
@objc private var title: String? {
willSet {
name = newValue
}
}
override class func ignoredProperties() -> [String] {
["title"]
}
override class func primaryKey() -> String? {
"id"
}
override required init() {
super.init()
}
}
let list: [Genres] = ([Genres].deserialize(from: JsonString, designatedPath: "genres") ?? []).compactMap { $0 }
list.forEach { print($0.id, $0.name) }
这里需要注意title属性前面加了@objc修饰表示这是OC属性,这样HandyJson在对title赋值时willSet将被调起,我们可以在willSet中对name进行赋值。如果不希望title属性也存储到Realm中,可以在Realm的ignoredProperties方法中标注。
执行代码后可以验证结果:
从Realm的表结构可以看出,只有id和name,title并不存在,其次name的值也是完整的,这也符合我们的预期。
2. Codable
let JsonString = """
{"genres":[{"id":28,"name":"Action"},{"id":12,"name":"Adventure"},{"id":16,"name":"Animation"},{"id":35,"name":"Comedy"},{"id":80,"name":"Crime"},{"id":99,"name":"Documentary"}]}
"""
class GenresModel: Object, Codable {
@Persisted(primaryKey: true) var id: Int
@Persisted var name: String?
override required init() {
super.init()
}
}
let list = ([GenresModel].decodeJSON(from: JsonString, designatedPath: "genres") ?? []).compactMap { $0 }
list.forEach { print($0.id, $0.name) }
这里Realm对象的属性可以使用@Persisted标注。使用Codable解析时需要注意匹配数据类型,不可以直接将id从Int转成String,当然你也可以写映射方法来实现。