代码之家  ›  专栏  ›  技术社区  ›  Optimalist

NSCoding和Codable属性<=>JSON格式<=>(读/写)文件

  •  0
  • Optimalist  · 技术社区  · 6 年前

    我需要从JSON格式的文件读取/写入可编码(例如,日期)和NSCoding(例如,NSMutableAttributedString)的属性。在研究了如何使用Codable读取和写入文件、如何使用JSON格式读取和写入文件,以及如何在某些属性不符合Codable(但确实符合NSCoding)时将NSCoding与Codable结合起来之后,我将下面的代码混在一起,并在这个过程中感到困惑。

    import Foundation
    
    class Content: Codable {
    
        // Content
        var attrStr = NSMutableAttributedString(string: "")
        var date: Date?
    
        // Initializer for content
        init(attrStr: NSMutableAttributedString, date: Date) {
            self.attrStr = attrStr
            self.date = date
    }
    
        // Need to explicitly define because NSMutableAttributedString isn't codable
        enum CodingKeys: String, CodingKey {
    
            case attrStr
            case date
        }
    
        // Need to explicitly define the decoder. . . .
        required init(from decoder: Decoder) throws {
    
            let container = try decoder.container(keyedBy: CodingKeys.self)
    
            date = try container.decode(Date.self, forKey: .date)
    
            let attrStrData = try container.decode(Data.self, forKey: .attrStr)
            attrStr = NSKeyedUnarchiver.unarchiveObject(with: attrStrData) as? NSMutableAttributedString ?? NSMutableAttributedString(string: "Error!")
        }
    
        // Need to explicitly define the encoder. . . .
        func encode(to encoder: Encoder) throws {
    
            var container = encoder.container(keyedBy: CodingKeys.self)
    
            try container.encode(date, forKey: .date)
    
            let attrStrData = NSKeyedArchiver.archivedData(withRootObject: attrStr)
            try container.encode(attrStrData, forKey: .attrStr)
        }
    
        static func getFileURL() -> URL {
    
            // Get the directory for the file
            let docsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
            // Get the full path and filename
            return docsDir.appendingPathComponent("contentArray").appendingPathExtension("cntnt")
        }
    
        static func saveToFile(content: [Content]) {
    
            // Get the file's URL
            let fileURL = getFileURL()
    
            do {
                // Encode the data
                let data = try JSONEncoder().encode(content)
                // Write to a/the file
                try data.write(to: fileURL)
    
            } catch {
                print("Could not encode or save to the file!")
            }
        }
    
        static func loadFromFile() -> [Content] {
    
            // Get the file's URL
            let fileURL = getFileURL()
    
            do {
                // Read from the file
                let data = try Data(contentsOf: fileURL)
                // Decode the data
                return try JSONDecoder().decode([Content].self, from: data)
    
            } catch {
                print("Could not decode or read from the file!")
                return []
            }
        }
    }
    
    1 回复  |  直到 6 年前
        1
  •  1
  •   Fabian    6 年前

    关于你的选择,我不知道怎么做。

    我给了实施 Codable 对于 NSMutableAttributedString Source

    class MutableAttributedStringContainer: Codable {
        let attributedString: NSMutableAttributedString
    
        init(attributedString: NSMutableAttributedString) {
            self.attributedString = attributedString
        }
    
        public required init(from decoder: Decoder) throws {
            let container = try decoder.singleValueContainer()
            let data = try container.decode(Data.self)
    
            let archiver = try NSKeyedUnarchiver(forReadingFrom: data)
            attributedString = NSMutableAttributedString(coder: archiver)!
        }
    
        public func encode(to encoder: Encoder) throws {
            let archiver = NSKeyedArchiver(requiringSecureCoding: true)
            attributedString.encode(with: archiver)
    
            var container = encoder.singleValueContainer()
            try container.encode(archiver.encodedData)
        }
    }
    

    下面是一个如何使用它的例子。

    func testing() {
        let attributedString = NSMutableAttributedString(string: "Hello world!")
        let attributedStringContainer = MutableAttributedStringContainer(attributedString: attributedString)
    
        // Necessary because encoding into a singleValueContainer() creates a
        // JSON fragment instead of a JSON dictionary that `JSONEncoder` wants
        // create.
        struct Welcome: Codable {
            var attributedString: MutableAttributedStringContainer
        }
        let welcome = Welcome(attributedString: attributedStringContainer)
    
        let encoder = JSONEncoder()
        encoder.outputFormatting = .prettyPrinted
        let data = try! encoder.encode(welcome)
        print(String(bytes: data, encoding: .utf8) as Any)
    
        let decoder = JSONDecoder()
        let welcome2 = try! decoder.decode(Welcome.self, from: data)
        print("decoded to string:", welcome2.attributedString.attributedString.string)
    }
    

    可编码的 结构相互建立。如果所有底层结构都实现 编译器可以自己创建编码和解码函数。如果没有,开发人员必须对它们进行编码并将它们放在 CodingKey ,同样用于解码。

    编码键 . 也许读一本 Raywenderlich Tutorial 可编码的 为了更好地理解它。

    应该有一个可识别的处理流,但我看不出这三种解码器/编码器是如何相互作用或相互替代的。

    NSCoding 与合作 NSKeyedUnarchiver/NSKeyedArchiver 和回报 NSData

    可编码的 与任何支持 可编码的 JSONEncoder/JSONDecoder ,它返回 Data 以人类可读的格式 JSON .utf8 .