解码在字符串中转义的 JSON 对象

Decode a JSON object escaped in a String

我收到 API 的回复(不幸的是,我无法更改它)看起来像(只是一个例子):

作为字节 => "{\"key\":\"value\"}"

开始和结束引号以及转义引号都是响应的一部分,我正在以一种非常丑陋的方式解决它,如下所示:

// (...) Receiving response

guard var responseString = String(bytes: data, encoding: .utf8) else {
    print("Response wasn't just a string, great!") // unfortunately, this never happens
    return
}
responseString = responseString.trimmingCharacters(in: .whitespacesAndNewlines) // make sure it is trimmed
responseString = String(responseString.dropFirst()) // drop the quote at the start
responseString = String(responseString.dropLast()) // drop the quote at the end
responseString = responseString.replacingOccurrences(of: "\\"", with: "\"")// convert all \" to " (and hope nothing else is escaped <<< this is really bad!!!)
let responseDataToDecode = responseString.data(using: .utf8)!

// (...) decoding with JSONDecoder

有没有办法自动对字符串进行反转义并使用其中包含的 JSON 对象?

第一步:您需要一份正式的书面声明,说明您的数据格式到底是什么。看起来有人拿了一些数据,把它变成 JSON 数据,将数据解释为字符串,然后将字符串转换为 JSON 片段。解码 JSON 片段并不难,获取一个字符串,将字符串转换为数据,并将该数据解码为 JSON (从 JSONSerialization 和 .allowFragments 开始,可能是唯一一次你应该在生活中使用 .allowFragments)。不发誓就很难做到。

但首先您要书面说明这实际上是格式。因为我敢打赌,负责该数据格式的人最终会在不告诉您的情况下更改它并破坏您的代码。

如果是双重编码,那么你只需要双重解码。如果我没理解错的话,传入的数据是这样的:

let str = #""{\"key\":\"value\"}""#
// "{\"key\":\"value\"}"

第一个字节是",第二个字节是{,第三个字节是\,第四个字节是"

这是一个 JSON 编码的字符串。所以将其解码为字符串(有一段时间这不起作用,因为它是 "fragment," 但目前它工作正常,至少在我所有的测试中):

let decoder = JSONDecoder()
let string = try! decoder.decode(String.self, from: Data(str.utf8)) // {"key":"value"}

然后将其解码为您的类型([String:String] 例如):

let result = try! decoder.decode([String:String].self, from: Data(string.utf8))
// ["key": "value"]

(IMO 这种双重编码很好,顺便说一句,我不确定为什么有这么多评论反对它。在许多情况下序列化任意对象比强制模式更有意义处理任意结构。只要它编码干净,我看不出这里有任何问题。)