The given data did not contain a top-level value error when attempting to decode json into NSManagedObject

牧云@^-^@ 提交于 2021-02-07 10:53:49

问题


While migrating an app from Realm to CoreData, I'm attempting to parse the following JSON structure:

{
    "username": "972542052677",
    "displayName": "Arik",
    "countryCode": "972",
    "status": "Busy",
    "walletPublicKey": "XgWDiGMFRPDRVnRq8nxu7NyMLuT4Uez7mJ",
    "lastSeen": "2020-08-05T08:12:57.5267881",
    "isOnline": false
}

I need to create an NSManagedObject from this data so I'm following this tutorial: https://medium.com/@andrea.prearo/working-with-codable-and-core-data-83983e77198e

This is the model. It inherits from the automatically generated "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)
}}

This is the decoder:

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
    }()

This is the part that does the actual decoding. T stands for ManagedProfileCodable, and the existingData variable holds a valid json (I encoded it successfully using the older JSONSerialization library to make sure it is valid)

        do
        {
            let result = try jsonDecoder.decode(T.self, from: existingData)
            DispatchQueue.main.async{
                completionHandler(NetworkResult<T>.success(result))}
        }
        catch let error {
...
        }

This is the error I receive:

▿ valueNotFound : 2 elements

  • .0 : TwoVerte.ManagedProfileCodable

▿ .1 : Context

  • codingPath : 0 elements
  • debugDescription : "The given data did not contain a top-level value."
  • underlyingError : nil

I came across an unanswered post from another developer who describes this same issue over here: CoreData NSManagedObject & JSON Encodable not working

Since the JSON is valid and the break point inside the initFromDecoder is reached, I can't think of anything that might cause this failure. Probably something is wrong in the way the class is built. I wonder if anyone else has also encountered a similar issue.


回答1:


I think issue is optional Bool type for isOnline. CoreData does not support saving optional primitives.

If you create NSManagedObject and add boolean property.

@NSManaged public var isOnline: Bool?

You will get error:

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

In most of the cases you should be able to make it non-optional and use default.

Since you can't add decode initialiser in extension. In this case, I suggest to set code generation to none.

Create ManagedProfile class and confirm to Codable.




回答2:


The reason it fails is because you have created a sub-class to your core data entity class but it needs to be the parent class you use since that is what you are instantiating here

self.init(entity: entity, insertInto: managedObjectContext)

So drop the sub-class and move your code to an extension

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



回答3:


I have finally resolved the issue. Instead using "Codegen: Class Definition", I should have used "Codegen: Manual" and add the subclass like this:

  1. Open the data model file
  2. Under Entities, select the relevant entity
  3. From the upper menu, choose Editor -> Create NSManagedObject subclass...


来源:https://stackoverflow.com/questions/63262517/the-given-data-did-not-contain-a-top-level-value-error-when-attempting-to-decode

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!