json 将核心数据保存到外部备份文件

ghhaqwfi  于 2023-07-01  发布在  其他
关注(0)|答案(1)|浏览(91)

我有一个旅行规划应用程序,我正在使用的核心数据的数据存储。我的许多实体中的两个实体(Person和Trip)具有多对多关系(一个人可以进行许多次旅行,而一次旅行包括许多人)。我想添加一个功能,让用户有机会创建一个外部备份文件的数据作为一个txt文件,可以住在iCloud,或在文件的位置。我将需要能够序列化和反序列化的备份和还原操作。
从我所读到的内容来看,JSON似乎只能处理一对多的关系(严格的层次关系),而不能处理多对多的关系。如果我错了,它确实可以进行多对多,有人能帮助我理解如何序列化它吗?
但如果事实上它不能,还有什么其他方法可以推荐用于将核心数据备份到txt文件中?理想情况下,这将是Swift拥有用于序列化和反序列化的原生方法。

mdfafbf1

mdfafbf11#

所以,你有这样的东西:

class Person: NSManagedObject {
    @NSManaged var id: UUID
    @NSManaged var trips: Set<Trip>
}

class Trip: NSManagedObject {
    @NSManaged var id: UUID
    @NSManaged var persons: Set<Person>
}

一种共享的方法(到第三方库、服务器,或者在您的情况下是副本)是将“多对多”转换为UUID数组。这样,如果需要,另一方只需要“重做”连接。
如果我们以JSON(但也可以是XML或任何其他格式)进行思考,一种方式是“一个大文件模式”

{
    "persons": [
                    { "id": "personId1", "trips": ["tripId1", 
                                                   "tripId2", 
                                                   "tripId3"]
                    },
                    { "id": "personId2", "trips": ["tripId3", 
                                                   "tripId5"]
                    },
                ],
    "trips": [ 
                 {"id": "tripId1", "persons": ["personId1"]},
                 {"id": "tripId2", "persons": ["personId1"]},
                 {"id": "tripId3", "persons": ["personId1", "personId2"]},
                 {"id": "tripId4", "persons": []},
                 {"id": "tripId5", "persons": ["personId2"]},
             ]
}

现在可以创建导出方法:

extension Person {
    func export() -> [String: Any] {
        ["id": id.uuidString,
         "trips": trips.map { $0.id.uuidString }]
    }
}

extension Trip {
    func export() -> [String: Any] {
        ["id": id.uuidString,
         "persons": persons.map { $0.id.uuidString }]
    }
}

你也可以迭代每个属性,以获得更“通用”的东西:

extension Person {
    func exportAttributes() -> [String: Any] {
        //might need a `context.perform()` here, it depends who calls it, from where, could be async method, or a with a completion, it's up to your own code
        entity.attributesByName.reduce(into: [String: Any]()) { $0[$1.key] = value(forKey: $1.key) }
    }
    
        //Sample with computing some values
    func exportAttributesWithExtraWork() -> [String: Any] {
                entity.attributesByName.reduce(into: [String: Any]()) {
            var finalKey = $1.key
            switch finalKey {
            case #keyPath(Person.id):
                finalKey = "personId" //Here, we want to have another key
            default:
                break
            }
            
            switch $1.value.attributeType {
            case .dateAttributeType:
                guard let date = value(forKey: $1.key) as? Date else { return }
                $0[finalKey] = date.timeIntervalSince1970 //It's just to show a sample where you change the value according to its type
            default:
                $0[finalKey] = value(forKey: $1.key)
            }
        }
    }
    
    //Sample for relations
    func exportRelations() -> [String: Any] {
        entity.relationshipsByName.reduce(into: [String: Any]()) {
            if let trips = value(forKey: $1.key) as? Set<Trips> {
                $0[$1.key] = trips.map { aTrip in aTrip.id.uuidString }
            }
        }
    }

    func fullExport() -> [String: Any] {
        var dictionary: [String: Any] = [:]
        let attributes = exportAttributes()
        dictionary.merge(attributes, uniquingKeysWith: { (_, new)  in return new })
        let relations = exportRelations()
        dictionary.merge(relations, uniquingKeysWith: { (_, new)  in return new })
        return dictionary 
    }
}

相关问题