hgl*_*hgl 43 core-data swift swift4 xcode9 codable
Codable似乎是一个非常令人兴奋的功能.但我想知道我们如何在核心数据中使用它?特别是,是否可以直接从/向NSManagedObject编码/解码JSON?
我尝试了一个非常简单的例子:
并定义Foo自己:
import CoreData
@objc(Foo)
public class Foo: NSManagedObject, Codable {}
Run Code Online (Sandbox Code Playgroud)
但是当它像这样使用时:
let json = """
{
"name": "foo",
"bars": [{
"name": "bar1",
}], [{
"name": "bar2"
}]
}
""".data(using: .utf8)!
let decoder = JSONDecoder()
let foo = try! decoder.decode(Foo.self, from: json)
print(foo)
Run Code Online (Sandbox Code Playgroud)
编译器因此错误而失败:
super.init isn't called on all paths before returning from initializer
Run Code Online (Sandbox Code Playgroud)
目标文件是定义的文件 Foo
我想我可能做错了,因为我甚至没有通过NSManagedObjectContext,但我不知道在哪里坚持下去.
核心数据是否支持Codable?
cas*_*ora 75
您可以将Codable接口与CoreData对象一起使用来对数据进行编码和解码,但是它不像使用普通的旧swift对象那样自动化.以下是如何使用Core Data对象直接实现JSON解码:
首先,您使对象实现Codable.必须在对象上定义此接口,而不是在扩展中定义.您还可以在此课程中定义编码密钥.
class MyManagedObject: NSManagedObject, Codable {
@NSManaged var property: String?
enum CodingKeys: String, CodingKey {
case property = "json_key"
}
}
Run Code Online (Sandbox Code Playgroud)
接下来,您可以定义init方法.这也必须在类方法中定义,因为Decodable协议需要init方法.
required convenience init(from decoder: Decoder) throws {
}
Run Code Online (Sandbox Code Playgroud)
但是,与托管对象一起使用的正确初始化程序是:
NSManagedObject.init(entity: NSEntityDescription, into context: NSManagedObjectContext)
Run Code Online (Sandbox Code Playgroud)
所以,这里的秘诀是使用userInfo字典将适当的上下文对象传递给初始化器.为此,您需要CodingUserInfoKey使用新键扩展结构:
extension CodingUserInfoKey {
static let context = CodingUserInfoKey(rawValue: "context")
}
Run Code Online (Sandbox Code Playgroud)
现在,您可以作为上下文的解码器:
required convenience init(from decoder: Decoder) throws {
guard let context = decoder.userInfo[CodingUserInfoKey.context!] as? NSManagedObjectContext else { fatalError() }
guard let entity = NSEntityDescription.entity(forEntityName: "MyManagedObject", in: context) else { fatalError() }
self.init(entity: entity, in: context)
let container = decoder.container(keyedBy: CodingKeys.self)
self.property = container.decodeIfPresent(String.self, forKey: .property)
}
Run Code Online (Sandbox Code Playgroud)
现在,当您为托管对象设置解码时,您需要传递正确的上下文对象:
let data = //raw json data in Data object
let context = persistentContainer.newBackgroundContext()
let decoder = JSONDecoder()
decoder.userInfo[.context] = context
_ = try decoder.decode(MyManagedObject.self, from: data) //we'll get the value from another context using a fetch request later...
try context.save() //make sure to save your data once decoding is complete
Run Code Online (Sandbox Code Playgroud)
要编码数据,您需要使用编码协议功能执行类似操作.
Jos*_*zzi 12
CoreData是它自己的持久性框架,根据其完整的文档,您必须使用其指定的初始化程序,并遵循相当具体的路径来创建和存储对象.
但是,您仍然可以使用Codable有限的方式使用它NSCoding.
一种方法是使用这些协议中的任何一个解码对象(或结构),并将其属性转移到NSManagedObject您为每个Core Data的文档创建的新实例中.
另一种方法(非常常见)是仅将一个协议用于要存储在托管对象属性中的非标准对象."非标准",我指的是任何不符合模型中指定的Core Data标准属性类型的东西.例如,NSColor不能直接存储为托管对象属性,因为它不是CD支持的基本属性类型之一.相反,您可以使用NSKeyedArchiver将颜色序列化为NSData实例并将其作为Data属性存储在Managed Object中.用这个过程逆转这个过程NSKeyedUnarchiver.这是简单的,有一个更好的方法来使用Core Data(参见瞬态属性),但它说明了我的观点.
您还可以设想采用Encodable(组合的两个协议之一Codable- 您能猜出另一个的名称吗?)将Managed Object实例直接转换为JSON进行共享,但是您必须指定编码键和自己的自定义encode实现它不会由编译器使用自定义编码密钥自动合成.在这种情况下,你要指定唯一要列入键(属性).
希望这可以帮助.
遵循casademora的解决方案,
guard let context = decoder.userInfo[.context] as? NSManagedObjectContext else { fatalError() }
应该
guard let context = decoder.userInfo[CodingUserInfoKey.context!] as? NSManagedObjectContext else { fatalError() }.
这可以防止Xcode错误地识别为阵列切片问题的错误.
编辑:使用隐式展开的选项,以消除.context每次使用时强制解包的需要.
作为那些想要使用 XCode 的现代NSManagedObject文件生成方法的人的替代方案,我创建了一个DecoderWrapper类来公开一个Decoder对象,然后我在符合JSONDecoding协议的对象中使用该对象:
class DecoderWrapper: Decodable {
let decoder:Decoder
required init(from decoder:Decoder) throws {
self.decoder = decoder
}
}
protocol JSONDecoding {
func decodeWith(_ decoder: Decoder) throws
}
extension JSONDecoding where Self:NSManagedObject {
func decode(json:[String:Any]) throws {
let data = try JSONSerialization.data(withJSONObject: json, options: [])
let wrapper = try JSONDecoder().decode(DecoderWrapper.self, from: data)
try decodeWith(wrapper.decoder)
}
}
extension MyCoreDataClass: JSONDecoding {
enum CodingKeys: String, CodingKey {
case name // For example
}
func decodeWith(_ decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
}
}
Run Code Online (Sandbox Code Playgroud)
这可能只对没有任何非可选属性的模型有用,但它解决了我想要使用Decodable但也管理与 Core Data 的关系和持久性的问题,而无需手动创建我的所有类/属性。
编辑:使用中的示例
如果我有一个 json 对象:
let myjson = [ "name" : "Something" ]
Run Code Online (Sandbox Code Playgroud)
我在 Core Data 中创建了对象(为了简洁起见,这里强制转换):
let myObject = NSEntityDescription.insertNewObject(forEntityName: "MyCoreDataClass", into: myContext) as! MyCoreDataClass
Run Code Online (Sandbox Code Playgroud)
我使用扩展来让对象解码 json:
do {
try myObject.decode(json: myjson)
}
catch {
// handle any error
}
Run Code Online (Sandbox Code Playgroud)
现在myObject.name是"Something"
| 归档时间: |
|
| 查看次数: |
16663 次 |
| 最近记录: |