【问题标题】:Decode a json containing HTML解码包含 HTML 的 json
【发布时间】:2019-08-12 13:16:47
【问题描述】:

我从 下面的示例代码中提到的 SO 套接字获取了一个真实的响应。我需要将其解码为某种类型的对象。所以我定义了两个可解码结构:SocketResponseQuestion。 然后我尝试使用默认的JSONDecoder 对其进行解码。

但由于 data 键包含大量无效的 json 格式,它无法正常工作并抛出此错误:

dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "给定的数据不是有效的 JSON。",underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840"没有字符串字符 2 周围对象中值的键。" UserInfo={NSDebugDescription=字符 2 周围对象中的值没有字符串键。})))

我也尝试了JSONSerialization,但它无法解码并引发类似错误。

那么我如何将这种类型的字符串解码为类型化对象而不丢失 HTML 格式?(因为响应可能不同,我们唯一可以传递的就是 data 值总是一个 String 包含一些 HTML 代码。)

- 游乐场:

这是您可以使用的简单重现代码:

struct SocketResponse<T: Decodable>: Decodable {
    let action: String
    let data: T
}

struct Question: Decodable {
    let id: String
    let body: String
    let tags: [String]
    let siteid: Int
    let noAnswers: Bool
    let hasBounty: Bool
    let fetch: Bool
}

let responseData = """
{
"action": "1-questions-newest-tag-ios",
"data": "{"id":"57238686","body":"<div class=\"question-summary\" id=\"question-summary-57238686\">\r\n <div class=\"statscontainer\">\r\n <div class=\"stats\">\r\n <div class=\"vote\">\r\n <div class=\"votes\">\r\n <span class=\"vote-count-post \"><strong>0</strong></span>\r\n <div class=\"viewcount\">votes</div>\r\n </div>\r\n </div>\r\n <div class=\"status unanswered\">\r\n <strong>0</strong>answers\r\n </div>\r\n </div>\r\n <div class=\"views \" title=\"1 view\">\r\n 1 view\r\n</div>\r\n </div>\r\n <div class=\"summary\">\r\n <h3><a href=\"/questions/57238686/how-to-realize-a-movable-controller-in-ipados-like-mail-app-in-ipad\" class=\"question-hyperlink\">How to realize a movable controller in iPadOS, like mail APP in iPad</a></h3>\r\n <div class=\"excerpt\">\r\n Mail App in iPad, when we create a new mail, the controller will be presented. But in iPadOS system, this controller can be moved, and if move it to the left side or right side, it can become a ...\r\n </div>\r\n <div class=\"tags t-ios t-swift t-ipad t-ipados\">\r\n <a href=\"/questions/tagged/ios\" class=\"post-tag\" title=\"show questions tagged &#39;ios&#39;\" rel=\"tag\">ios</a> <a href=\"/questions/tagged/swift\" class=\"post-tag\" title=\"show questions tagged &#39;swift&#39;\" rel=\"tag\">swift</a> <a href=\"/questions/tagged/ipad\" class=\"post-tag\" title=\"show questions tagged &#39;ipad&#39;\" rel=\"tag\">ipad</a> <a href=\"/questions/tagged/ipados\" class=\"post-tag\" title=\"show questions tagged &#39;ipados&#39;\" rel=\"tag\">ipados</a> \r\n </div>\r\n <div class=\"started fr\">\r\n <div class=\"user-info \">\r\n <div class=\"user-action-time\">\r\n <a href=\"/questions/57238686/how-to-realize-a-movable-controller-in-ipados-like-mail-app-in-ipad\" class=\"started-link\">asked <span title=\"2019-07-28 07:08:46Z\" class=\"relativetime\">just now</span></a>\r\n </div>\r\n <div class=\"user-gravatar32\">\r\n <a href=\"/users/11847413/peipei\"><div class=\"gravatar-wrapper-32\"><img src=\"https://lh6.googleusercontent.com/-RJzULVcP1iM/AAAAAAAAAAI/AAAAAAAAAAA/ACHi3reW_khZzpeiqEPv6Cww36Wohd_oUg/photo.jpg?sz=32\" alt=\"\" width=\"32\" height=\"32\"></div></a>\r\n </div>\r\n <div class=\"user-details\">\r\n <a href=\"/users/11847413/peipei\">peipei</a>\r\n <div class=\"-flair\">\r\n <span class=\"reputation-score\" title=\"reputation score \" dir=\"ltr\">1</span><span title=\"1 bronze badge\" aria-hidden=\"true\"><span class=\"badge3\"></span><span class=\"badgecount\">1</span></span><span class=\"v-visible-sr\">1 bronze badge</span>\r\n </div>\r\n </div>\r\n</div>\r\n </div>\r\n </div>\r\n</div>","tags":["ios","swift","ipad","ipados"],"siteid":1,"noAnswers":true,"hasBounty":false,"fetch":false}"
}

""".data(using: .utf8)!
do {
    let json = try JSONDecoder().decode(SocketResponse<Question>.self, from: responseData)
    print(json)
} catch {
    print(error)
}

【问题讨论】:

  • action & data 必须用引号括起来才能成为有效的 JSON。
  • data: "{"id":"57238686" json 在哪里开始和结束?!
  • 他们需要报价,但不足以成为有效的 json @Dementic
  • @Liam 在行尾"fetch":false}"
  • 你还需要转义"id":"57238686","body"

标签: html json swift nsjsonserialization decodable


【解决方案1】:

已编辑

我理解你。在解码数据之前执行简单的响应适配:

struct SocketResponse<T: Decodable>: Decodable {
    let action: String
    let data: T
}


struct Question: Decodable {
    let id: String
    let body: String
    let tags: [String]
    let siteid: Int
    let noAnswers: Bool
    let hasBounty: Bool
    let fetch: Bool
}

let fileURL = Bundle.main.url(forResource: "SO", withExtension: "txt")
var responseStr = try String(contentsOf: fileURL!, encoding: String.Encoding.utf8)

responseStr = (responseStr as NSString).replacingOccurrences(of: "\r\n", with: "")
responseStr = (responseStr as NSString).replacingOccurrences(of: "\n", with: "")
responseStr = (responseStr as NSString).replacingOccurrences(of: "\"{\"", with: "{\"")
responseStr = (responseStr as NSString).replacingOccurrences(of: "}\"}", with: "}}")


let responseData = responseStr.data(using: .utf8)!

do {
    let json = try JSONDecoder().decode(SocketResponse<Question>.self, from: responseData)
    print("Success", json)
} catch {
    print(error)
}

P.S.请注意,构造 \" 已转换为 "。即使在三方括号中,您也需要使用\\"

【讨论】:

  • 我不认为将Decodable 更改为Codable 是这里的关键。它仅用于编码和查找diff。谢谢你提到这一点。但我已经看到了,正如我所说,这是来自 StackOverflow 的实际工作套接字,我没有任何权力来更改响应数据。所以我正在寻找一个工作映射器,从这个映射到正确的可解码 JSON 或类似的东西。
  • @MojtabaHosseini 抱歉,忘记删除 \r\n。请立即检查
  • 它与您提供的 responseStr 一起使用。但与我发布的原始错误相同,它们略有不同。
  • @MojtabaHosseini 你能给出响应的文件示例吗?正如我提到的\" 转换为",最终没有办法区分。
  • 当然。 Here is a fresh one。这是原始回复。
猜你喜欢
  • 2020-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-03
  • 2011-10-27
  • 1970-01-01
相关资源
最近更新 更多