尝试将 json 解码为 NSManagedObject 时,给定数据不包含顶级值错误
The given data did not contain a top-level value error when attempting to decode json into NSManagedObject
将应用程序从 Realm 迁移到 CoreData 时,我正在尝试解析以下 JSON 结构:
{
"username": "972542052677",
"displayName": "Arik",
"countryCode": "972",
"status": "Busy",
"walletPublicKey": "XgWDiGMFRPDRVnRq8nxu7NyMLuT4Uez7mJ",
"lastSeen": "2020-08-05T08:12:57.5267881",
"isOnline": false
}
我需要根据这些数据创建一个 NSManagedObject,所以我正在学习本教程:
https://medium.com/@andrea.prearo/working-with-codable-and-core-data-83983e77198e
这是模型。它继承自自动生成的“ManagedProfile”class:
class ManagedProfileCodable: ManagedProfile, Codable {
enum CodingKeys: String, CodingKey {
case username = "username"
case displayName = "displayName"
case email = "email"
case status = "status"
case walletPublicKey = "walletPublicKey"
case countryCode = "countryCode"
case isOnline = "isOnline"
case lastSeen = "lastSeen"
}
// MARK: - Decodable
required convenience init(from decoder: Decoder) throws {
guard let codingUserInfoKeyManagedObjectContext = CodingUserInfoKey.managedObjectContext else{
fatalError("Failed to get codingUserInfoKeyManagedObjectContext")
}
guard let managedObjectContext = decoder.userInfo[codingUserInfoKeyManagedObjectContext] as? NSManagedObjectContext else{
fatalError("Failed to get context from decoder")
}
guard let entity = NSEntityDescription.entity(forEntityName: "ManagedProfile", in: managedObjectContext) else {
fatalError("Failed to read entity desc")
}
self.init(entity: entity, insertInto: managedObjectContext)
let container = try decoder.container(keyedBy: CodingKeys.self)
self.username = try container.decodeIfPresent(String.self, forKey: .username)
self.displayName = try container.decodeIfPresent(String.self, forKey: .displayName)
self.email = try container.decodeIfPresent(String.self, forKey: .email)
self.status = try container.decodeIfPresent(String.self, forKey: .status)
self.walletPublicKey = try container.decodeIfPresent(String.self, forKey: .walletPublicKey)
self.isOnline = try container.decodeIfPresent(Bool.self, forKey: .isOnline) ?? false
self.countryCode = try container.decodeIfPresent(String.self, forKey: .countryCode)
}
// MARK: - Encodable
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(username, forKey: .username)
try container.encode(username, forKey: .displayName)
try container.encode(username, forKey: .email)
try container.encode(username, forKey: .countryCode)
try container.encode(username, forKey: .lastSeen)
try container.encode(username, forKey: .walletPublicKey)
try container.encode(username, forKey: .status)
try container.encode(username, forKey: .isOnline)
}}
这是解码器:
var jsonDecoder : JSONDecoder = {
let decoder = JSONDecoder()
if let appDelegate = UIApplication.shared.delegate as? AppDelegate{
if let codingUserInfoKeyManagedObjectContext = CodingUserInfoKey.managedObjectContext{
decoder.userInfo[codingUserInfoKeyManagedObjectContext] = appDelegate.persistentContainer.viewContext
}
}
return decoder
}()
这是进行实际解码的部分。 T 代表 ManagedProfileCodable,existingData 变量包含一个有效的 json(我使用较旧的 JSONSerialization 库成功对其进行了编码以确保其有效)
do
{
let result = try jsonDecoder.decode(T.self, from: existingData)
DispatchQueue.main.async{
completionHandler(NetworkResult<T>.success(result))}
}
catch let error {
...
}
这是我收到的错误:
▿ valueNotFound : 2 个元素
- .0 : TwoVerte.ManagedProfileCodable
▿ .1 : 上下文
- 编码路径:0 个元素
- debugDescription:“给定的数据不包含顶级值。”
- 底层错误:无
我发现另一位开发者未回答 post,他在此处描述了同样的问题:CoreData NSManagedObject & JSON Encodable not working
由于 JSON 有效并且达到了 initFromDecoder 内部的断点,我想不出任何可能导致此失败的原因。 class 的构建方式可能有问题。不知道有没有人也遇到过类似的问题。
它失败的原因是因为您已经为您的核心数据实体 class 创建了一个 sub-class 但它必须是您使用的父 class 因为这就是您的身份在这里实例化
self.init(entity: entity, insertInto: managedObjectContext)
所以删除 sub-class 并将您的代码移至扩展程序
extension ManagedProfile: Codable {
//all code from ManagedProfileCodable
}
我认为问题是 isOnline 的可选 Bool 类型。 CoreData 不支持保存可选基元。
如果创建 NSManagedObject 并添加布尔值 属性.
@NSManaged public var isOnline: Bool?
你会得到错误:
property cannot be marked @NSManaged because its type cannot be represented in Objective-C
在大多数情况下,您应该能够做到 non-optional 并使用默认值。
因为您不能在扩展中添加解码初始化程序。在这种情况下,我建议将代码生成设置为 none
.
创建 ManagedProfile class 并确认 Codable。
我终于解决了这个问题。而不是使用“Codegen:Class Definition”,我应该使用“Codegen:Manual”并像这样添加子类:
- 打开数据模型文件
- 在实体下,select 相关实体
- 从上方菜单中,选择编辑器 -> 创建 NSManagedObject 子类...
将应用程序从 Realm 迁移到 CoreData 时,我正在尝试解析以下 JSON 结构:
{
"username": "972542052677",
"displayName": "Arik",
"countryCode": "972",
"status": "Busy",
"walletPublicKey": "XgWDiGMFRPDRVnRq8nxu7NyMLuT4Uez7mJ",
"lastSeen": "2020-08-05T08:12:57.5267881",
"isOnline": false
}
我需要根据这些数据创建一个 NSManagedObject,所以我正在学习本教程: https://medium.com/@andrea.prearo/working-with-codable-and-core-data-83983e77198e
这是模型。它继承自自动生成的“ManagedProfile”class:
class ManagedProfileCodable: ManagedProfile, Codable {
enum CodingKeys: String, CodingKey {
case username = "username"
case displayName = "displayName"
case email = "email"
case status = "status"
case walletPublicKey = "walletPublicKey"
case countryCode = "countryCode"
case isOnline = "isOnline"
case lastSeen = "lastSeen"
}
// MARK: - Decodable
required convenience init(from decoder: Decoder) throws {
guard let codingUserInfoKeyManagedObjectContext = CodingUserInfoKey.managedObjectContext else{
fatalError("Failed to get codingUserInfoKeyManagedObjectContext")
}
guard let managedObjectContext = decoder.userInfo[codingUserInfoKeyManagedObjectContext] as? NSManagedObjectContext else{
fatalError("Failed to get context from decoder")
}
guard let entity = NSEntityDescription.entity(forEntityName: "ManagedProfile", in: managedObjectContext) else {
fatalError("Failed to read entity desc")
}
self.init(entity: entity, insertInto: managedObjectContext)
let container = try decoder.container(keyedBy: CodingKeys.self)
self.username = try container.decodeIfPresent(String.self, forKey: .username)
self.displayName = try container.decodeIfPresent(String.self, forKey: .displayName)
self.email = try container.decodeIfPresent(String.self, forKey: .email)
self.status = try container.decodeIfPresent(String.self, forKey: .status)
self.walletPublicKey = try container.decodeIfPresent(String.self, forKey: .walletPublicKey)
self.isOnline = try container.decodeIfPresent(Bool.self, forKey: .isOnline) ?? false
self.countryCode = try container.decodeIfPresent(String.self, forKey: .countryCode)
}
// MARK: - Encodable
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(username, forKey: .username)
try container.encode(username, forKey: .displayName)
try container.encode(username, forKey: .email)
try container.encode(username, forKey: .countryCode)
try container.encode(username, forKey: .lastSeen)
try container.encode(username, forKey: .walletPublicKey)
try container.encode(username, forKey: .status)
try container.encode(username, forKey: .isOnline)
}}
这是解码器:
var jsonDecoder : JSONDecoder = {
let decoder = JSONDecoder()
if let appDelegate = UIApplication.shared.delegate as? AppDelegate{
if let codingUserInfoKeyManagedObjectContext = CodingUserInfoKey.managedObjectContext{
decoder.userInfo[codingUserInfoKeyManagedObjectContext] = appDelegate.persistentContainer.viewContext
}
}
return decoder
}()
这是进行实际解码的部分。 T 代表 ManagedProfileCodable,existingData 变量包含一个有效的 json(我使用较旧的 JSONSerialization 库成功对其进行了编码以确保其有效)
do
{
let result = try jsonDecoder.decode(T.self, from: existingData)
DispatchQueue.main.async{
completionHandler(NetworkResult<T>.success(result))}
}
catch let error {
...
}
这是我收到的错误:
▿ valueNotFound : 2 个元素
- .0 : TwoVerte.ManagedProfileCodable
▿ .1 : 上下文
- 编码路径:0 个元素
- debugDescription:“给定的数据不包含顶级值。”
- 底层错误:无
我发现另一位开发者未回答 post,他在此处描述了同样的问题:CoreData NSManagedObject & JSON Encodable not working
由于 JSON 有效并且达到了 initFromDecoder 内部的断点,我想不出任何可能导致此失败的原因。 class 的构建方式可能有问题。不知道有没有人也遇到过类似的问题。
它失败的原因是因为您已经为您的核心数据实体 class 创建了一个 sub-class 但它必须是您使用的父 class 因为这就是您的身份在这里实例化
self.init(entity: entity, insertInto: managedObjectContext)
所以删除 sub-class 并将您的代码移至扩展程序
extension ManagedProfile: Codable {
//all code from ManagedProfileCodable
}
我认为问题是 isOnline 的可选 Bool 类型。 CoreData 不支持保存可选基元。
如果创建 NSManagedObject 并添加布尔值 属性.
@NSManaged public var isOnline: Bool?
你会得到错误:
property cannot be marked @NSManaged because its type cannot be represented in Objective-C
在大多数情况下,您应该能够做到 non-optional 并使用默认值。
因为您不能在扩展中添加解码初始化程序。在这种情况下,我建议将代码生成设置为 none
.
创建 ManagedProfile class 并确认 Codable。
我终于解决了这个问题。而不是使用“Codegen:Class Definition”,我应该使用“Codegen:Manual”并像这样添加子类:
- 打开数据模型文件
- 在实体下,select 相关实体
- 从上方菜单中,选择编辑器 -> 创建 NSManagedObject 子类...