如何使用 RealmSwift 存储字典?

Posted

技术标签:

【中文标题】如何使用 RealmSwift 存储字典?【英文标题】:How can I store a Dictionary with RealmSwift? 【发布时间】:2016-02-21 03:18:57 【问题描述】:

考虑以下模型:

class Person: Object 
    dynamic var name = ""
    let hobbies = Dictionary<String, String>()

我正在尝试在 Realm 中存储一个 [String:String] 类型的对象,我从 Alamofire 请求中获得,但由于 hobbies 必须通过 let 定义,所以不能到 RealmSwift 文档,因为它是一种 List&lt;T&gt;/Dictionary&lt;T,U&gt; 类型。

let hobbiesToStore: [String:String]
// populate hobbiestoStore
let person = Person()
person.hobbies = hobbiesToStore

我也尝试重新定义init(),但总是以致命错误或其他错误告终。

如何在 RealSwift 中简单地复制或初始化字典? 我在这里错过了一些琐碎的事情吗?

【问题讨论】:

【参考方案1】:

Dictionary 不支持作为 Realm 中的属性类型。 您需要引入一个新类,其对象描述每个键值对和一对多关系,如下所示:

class Person: Object 
    dynamic var name = ""
    let hobbies = List<Hobby>()


class Hobby: Object 
    dynamic var name = ""
    dynamic var descriptionText = ""

对于反序列化,您需要将 JSON 中的字典结构映射到 Hobby 对象,并将键和值分配给适当的属性。

【讨论】:

谢谢!我也想过这个解决方案(因为它是最干净的解决方案),但不能在 RealmSwift 中使用任何 Swift 结构真的很令人沮丧......(甚至不是元组:()。因为我的数据真的是静态的,而且很简单,我最终将两个字符串与一个分隔符合并在一起,并创建了一个 List&lt;String&gt; 有一些限制使我们无法支持任何通用的 Swift 结构,尤其是元组。其中包括我们必须能够在运行时确定类型并能够通过动态访问器返回值。这不适用于元组。 2021 年更新:现在支持 Map 类型。请参阅下面的my answer。【参考方案2】:

也许效率有点低,但对我有用(来自 Int->String 的示例字典,与您的示例类似):

class DictObj: Object 
   var dict : [Int:String] 
      get 
         if _keys.isEmpty return [:] // Empty dict = default; change to other if desired
         else 
            var ret : [Int:String] = [:];
            Array(0..<(_keys.count)).map ret[_keys[$0].val] = _values[$0].val ;
            return ret;
         
      
      set 
         _keys.removeAll()
         _values.removeAll()
         _keys.appendContentsOf(newValue.keys.map( IntObj(value: [$0]) ))
         _values.appendContentsOf(newValue.values.map( StringObj(value: [$0]) ))
      
   
   var _keys = List<IntObj>();
   var _values = List<StringObj>();

   override static func ignoredProperties() -> [String] 
      return ["dict"];
   

Realm 无法存储 Strings/Ints 列表,因为它们不是对象,所以制作“假对象”:

class IntObj: Object 
   dynamic var val : Int = 0;


class StringObj: Object 
   dynamic var val : String = "";

受另一个关于堆栈溢出的答案的启发,类似地存储数组(帖子目前正在躲避我)...

【讨论】:

如果你有一个带有 time Int 或 Double 等的值,你会怎么做?最好的解决方案是使用 Data 对象和 JSONSerialization。【参考方案3】:

我目前正在模拟这个,方法是在我的模型上公开一个被忽略的 Dictionary 属性,由一个私有的、持久的 NSData 支持,该 NSData 封装了字典的 JSON 表示:

class Model: Object 
    private dynamic var dictionaryData: NSData?
    var dictionary: [String: String] 
        get 
            guard let dictionaryData = dictionaryData else 
                return [String: String]()
            
            do 
                let dict = try NSJSONSerialization.JSONObjectWithData(dictionaryData, options: []) as? [String: String]
                return dict!
             catch 
                return [String: String]()
            
        

        set 
            do 
                let data = try NSJSONSerialization.dataWithJSONObject(newValue, options: [])
                dictionaryData = data
             catch 
                dictionaryData = nil
            
        
    

    override static func ignoredProperties() -> [String] 
        return ["dictionary"]
    

这可能不是最有效的方法,但它让我可以继续使用Unbox 快速轻松地将传入的 JSON 数据映射到我的本地 Realm 模型。

【讨论】:

请注意额外的 JSON(反)序列化对性能的影响,并且您失去了以这种方式查询字典的能力。 嗨@mar​​ius,当然。这是一种解决方法,正如我所说,这不是最有效的方法,但它适用于我需要在我的领域有字典引用的情况(我真的不需要查询)。希望我们能在某个时候看到对字典的原生支持,在这种情况下就不再需要它了。【参考方案4】:

我会将字典保存为 Realm 中的 JSON 字符串。然后检索 JSON 并转换为字典。使用以下扩展。

extension String
func dictionaryValue() -> [String: AnyObject]

    if let data = self.data(using: String.Encoding.utf8) 
        do 
            let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: AnyObject]
            return json!

         catch 
            print("Error converting to JSON")
        
    
    return NSDictionary() as! [String : AnyObject]
 

extension NSDictionary
    func JsonString() -> String
    
        do
        let jsonData: Data = try JSONSerialization.data(withJSONObject: self, options: .prettyPrinted)
        return String.init(data: jsonData, encoding: .utf8)!
        
        catch
        
            return "error converting"
        
    

【讨论】:

很好的解决方案! 这是最简单的解决方案,使在 Realm 中存储字典变得容易。 也很慢,会使用keyarchiver【参考方案5】:

2021 年更新

从 Realm 10.8.0 开始,可以使用 Map 类型将字典存储在 Realm 对象中。

来自official documentation的示例:

class Dog: Object 
    @objc dynamic var name = ""
    @objc dynamic var currentCity = ""
    // Map of city name -> favorite park in that city
    let favoriteParksByCity = Map<String, String>()

【讨论】:

如何解码/编码地图?我写 Map 时出错:Type 'QuestionList' does not conform to protocol 'Decodable' || Type 'QuestionList' does not conform to protocol 'Encodable'

以上是关于如何使用 RealmSwift 存储字典?的主要内容,如果未能解决你的问题,请参考以下文章

iOS 14.0 之后如何解决 SwiftUI 中 RealmSwift.App 和 SwiftUI.App 的冲突?

如何在RealmSwift中避免迁移

如何使用 Realm swift 在本地设备上存储资产?

我们如何比较 RealmSwift List 和 Json 嵌套数组中的值?

如何使用 QSettings 存储 Python 字典

如何在 Realm Swift 中更新对象