Swift NSKeyedUnarchiver 在从字符串转换为数据后失败
Swift NSKeyedUnarchiver Failing After Converting from String to Data
我有一个符合 NSCoding 的 class。我想将 class 转换为数据,然后转换为可以存储在后端的字符串。
问题是,一旦它从 String
转换回 Data
,它就无法使用 NSKeyedUnarchiver
正确解档,因此转换为字符串一定会破坏数据。
代码如下:
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: drawItems, requiringSecureCoding: false)
let stringData = String(decoding: data, as: UTF8.self)
print("HERE successfully converted to a string: ", stringData)
let stringBackToData: Data? = stringData.data(using: .utf8)
if let stringBackToData = stringBackToData, let allItems = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(stringBackToData) as? [DrawItem] {
print("HERE items: ", allItems)
} else {
print("HERE FAILED to unarchive")
}
catch {
print("Failed: ", error)
}
这个字符串转换有什么问题?
默认outputFormat
for NSKeyedArchiver
is .binary
, which is not convertible to or from UTF-8. In your example, String(decoding: data, as: UTF8.self)
"repairs" invalid UTF-8 bytes by replacing them with the UTF-8 replacement character(0xFFFD
).
这会破坏存档数据,并阻止其正确解码。
如果您确实无法将二进制数据按原样传输到您的后端,或者无法以这种方式存储,并且必须将数据转换为字符串,您有两个选择:
NSKeyedArchiver
有 .xml
的备用 .outputFormat
,它生成 UTF-8 XML 数据。您可以通过创建归档器并对其进行配置来生成此数据:
let archiver = NSKeyedArchiver(requiringSecureCoding: false)
archiver.outputFormat = .xml
archiver.encode(drawItems, forKey: NSKeyedArchiveRootObjectKey)
if let error = archiver.error {
// Handle the error. This is the same error that would be produced
// by `NSKeyedArchiver.archivedData(withRootObject:requiringSecureCoding:)`
} else {
let data = archiver.encodedData
// `data` is UTF-8 XML data ready to be transmitted/stored
}
或者,您可以继续使用现在的便捷方法,但是Base64 encode它:
let data = try NSKeyedArchiver.archivedData(withRootObject: drawItems, requiringSecureCoding: false)
let base64EncodedData = data.base64EncodedString()
您使用哪种取决于您:就其本身而言,base64 编码的字符串比原始 XML 数据短(因为起始二进制数据明显小于其等效 XML ),但 XML 数据确实比 base64 数据压缩得好得多,而且很可能最终变得更小。您可以测试这两种方法,看看哪种方法更适合您的用例。
我有一个符合 NSCoding 的 class。我想将 class 转换为数据,然后转换为可以存储在后端的字符串。
问题是,一旦它从 String
转换回 Data
,它就无法使用 NSKeyedUnarchiver
正确解档,因此转换为字符串一定会破坏数据。
代码如下:
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: drawItems, requiringSecureCoding: false)
let stringData = String(decoding: data, as: UTF8.self)
print("HERE successfully converted to a string: ", stringData)
let stringBackToData: Data? = stringData.data(using: .utf8)
if let stringBackToData = stringBackToData, let allItems = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(stringBackToData) as? [DrawItem] {
print("HERE items: ", allItems)
} else {
print("HERE FAILED to unarchive")
}
catch {
print("Failed: ", error)
}
这个字符串转换有什么问题?
默认outputFormat
for NSKeyedArchiver
is .binary
, which is not convertible to or from UTF-8. In your example, String(decoding: data, as: UTF8.self)
"repairs" invalid UTF-8 bytes by replacing them with the UTF-8 replacement character(0xFFFD
).
这会破坏存档数据,并阻止其正确解码。
如果您确实无法将二进制数据按原样传输到您的后端,或者无法以这种方式存储,并且必须将数据转换为字符串,您有两个选择:
NSKeyedArchiver
有.xml
的备用.outputFormat
,它生成 UTF-8 XML 数据。您可以通过创建归档器并对其进行配置来生成此数据:let archiver = NSKeyedArchiver(requiringSecureCoding: false) archiver.outputFormat = .xml archiver.encode(drawItems, forKey: NSKeyedArchiveRootObjectKey) if let error = archiver.error { // Handle the error. This is the same error that would be produced // by `NSKeyedArchiver.archivedData(withRootObject:requiringSecureCoding:)` } else { let data = archiver.encodedData // `data` is UTF-8 XML data ready to be transmitted/stored }
或者,您可以继续使用现在的便捷方法,但是Base64 encode它:
let data = try NSKeyedArchiver.archivedData(withRootObject: drawItems, requiringSecureCoding: false) let base64EncodedData = data.base64EncodedString()
您使用哪种取决于您:就其本身而言,base64 编码的字符串比原始 XML 数据短(因为起始二进制数据明显小于其等效 XML ),但 XML 数据确实比 base64 数据压缩得好得多,而且很可能最终变得更小。您可以测试这两种方法,看看哪种方法更适合您的用例。