让我们从总结一些细节开始.Instagram正在将字符串"亲爱的朋友"
编码为"\u00d0\u0094\u00d0\u00be\u00d1\u0080\u00d0\u00be\u00d0\u00b3\u00d0\u00be\u00d0\u00b9 \u00d0\u00b4\u00d1\u0080\u00d1\u0083\u00d0\u00b3"
让我们看看这意味着什么.Д
是Unicode字符U+0414.它的UTF-8编码为D0 94
.请注意,JSON中的编码标题以\u00d0\u0094
开头.о
是Unicode字符U+043 E,UTF-8编码为D0 BE
.可以肯定的是,JSON中的编码标题有\u00d0\u00be
作为下一组值.因此,Instagram似乎将字符串编码为UTF-8,同时使用\uxxxx
个转义字符.至少对于西里尔字符.空格被编码为常规空格字符.
问题是,JSONDecoder
期望如果字符串包含表单\uxxxx
中的转义字符,则它假定代码是Unicode值,而不是UTF-8编码的一部分.当它解析标题时,它首先看到\u00d0
.这是Unicode字符Ð
.然后它看到了\u0094
.这是Unicode字符"Cancel Character",一个不可打印的字符.这种情况继续下go ,你最终得到"ÐоÑогой дÑÑг"
.
JSONDecoder
没有内置的功能来告诉它如何处理Instagram的非标准字符串编码.因此,这意味着唯一的解决方案是编写一个定制的解码器.
以下是一个可行的解决方案.按如下方式更新您的Media
struct :
struct Media: Codable {
let title: String
init(title: String) {
self.title = title
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let str = try container.decode(String.self, forKey: .title)
let data = Data(str.reduce([], { partialResult, char in
char.unicodeScalars.reduce(into: partialResult) { partialResult, scalar in
partialResult.append(UInt8(scalar.value))
}
}))
let res = String(data: data, encoding: .utf8)
self.title = res ?? "" // some fallback as desired
}
}
如果只有一个值需要处理,这是可以的.如果您需要为多个属性处理此问题,请将逻辑移至String
扩展:
extension String {
var fromInstagramEncoding: String? {
let data = Data(self.reduce([], { partialResult, char in
char.unicodeScalars.reduce(into: partialResult) { partialResult, scalar in
partialResult.append(UInt8(scalar.value))
}
}))
return String(data: data, encoding: .utf8)
}
}
然后更新后的Media
代码变为:
struct Media: Codable {
let title: String
init(title: String) {
self.title = title
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let str = try container.decode(String.self, forKey: .title)
self.title = str.fromInstagramEncoding ?? "" // some fallback as desired
}
}
下面是一个可以在playground 上运行的完整示例:
struct BlogPost: Codable {
let media: [Media]
}
struct Media: Codable {
let title: String
init(title: String) {
self.title = title
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let str = try container.decode(String.self, forKey: .title)
self.title = str.fromInstagramEncoding ?? ""
}
}
extension String {
var fromInstagramEncoding: String? {
let data = Data(self.reduce([], { partialResult, char in
char.unicodeScalars.reduce(into: partialResult) { partialResult, scalar in
partialResult.append(UInt8(scalar.value))
}
}))
return String(data: data, encoding: .utf8)
}
}
let instagramJSON = """
[
{
"media": [
{
"title" : "\\u00d0\\u0094\\u00d0\\u00be\\u00d1\\u0080\\u00d0\\u00be\\u00d0\\u00b3\\u00d0\\u00be\\u00d0\\u00b9 \\u00d0\\u00b4\\u00d1\\u0080\\u00d1\\u0083\\u00d0\\u00b3"
}
]
}
]
"""
let badData = instagramJSON.data(using: .utf8)!
let result = try JSONDecoder().decode([BlogPost].self, from: badData)
print(result[0].media[0].title)
输出:
亲爱的朋友
请注意,此解决方案适用于所提供的示例.Instagram对某些字符的编码方式可能会导致该解决方案在某些情况下失败.如果没有更多的数据,我就不能确定.如果您遇到此代码不能正确处理的示例,请发表带有相关详细信息的 comments .