尝试将json解码为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 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,并且existData变量包含一个有效的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:上下文

  • codingPath:0个元素
  • debugDescription:“给定的数据不包含顶级值。”
  • underlyingError:无

我遇到了另一位开发人员的未回答帖子,他在此处描述了相同的问题:CoreData NSManagedObject & JSON Encodable not working

由于JSON有效且到达了initFromDecoder内部的断点,所以我想不出任何可能导致此失败的原因。建立类的方式可能出了问题。我想知道是否还有其他人也遇到过类似的问题。

iCMS 回答:尝试将json解码为NSManagedObject时,给定数据不包含顶级值错误

失败的原因是因为您已经为核心数据实体类创建了一个子类,但是它必须是您使用的父类,因为这就是您要在此处实例化的

extension ManagedProfile: Codable {
     //all code from ManagedProfileCodable
}

因此删除子类,然后将代码移至扩展名

{{1}}
,

我认为问题是isOnline的可选Bool类型。 CoreData不支持保存可选原语。

如果创建NSManagedObject并添加布尔属性。

@NSManaged public var isOnline: Bool?

您会收到错误消息:

property cannot be marked @NSManaged because its type cannot be represented in Objective-C

在大多数情况下,您应该可以使其成为非可选并使用默认值。

由于您无法在扩展名中添加解码初始化程序。在这种情况下,我建议将代码生成设置为none

创建ManagedProfile类并确认为Codable。

,

我终于解决了这个问题。代替使用“ Codegen:类定义”,我应该使用“ Codegen:手动”并添加如下子类:

  1. 打开数据模型文件
  2. 在实体下,选择相关实体
  3. 从上方菜单中选择“编辑器”->“创建NSManagedObject子类...
本文链接:https://www.f2er.com/1802050.html

大家都在问