0

I have a real response fetched from SO's socket mentioned in the sample code below. I need to decode it to some type of object. So I defined two decodable structs: SocketResponse and Question. Then I tried to decode it with a default JSONDecoder.

But since the data key contains massive invalid json formatting, it is not going to work and throwing me this error:

dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "No string key for value in object around character 2." UserInfo={NSDebugDescription=No string key for value in object around character 2.})))

I tried the JSONSerialization as well but it couldn't decode it and throwing a similar error.

So how can I decode this type of string into the the typed object without loosing the HTML formatting? (Since responses may be different and the only thing we can relay on is the data value is always a String containing some HTML code.)

- The Playground:

Here is the simple reproducing code you can play with:

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)
}
Mojtaba Hosseini
  • 95,414
  • 31
  • 268
  • 278

1 Answers1

2

Edited

I understand you. Before decode data perform simple adaptation of response:

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. please note that construction \" is transformed to ". You need to use \\" even in triple brackets.

Gralex
  • 4,285
  • 7
  • 26
  • 47
  • I don't think changing `Decodable` to `Codable` is a key here. It's needed only for encoding and looking for `diff`. Thanks you for mentioning that. But I already saw that and as I said, This is from actual StackOverflow's working socket and I don't have any power to change the response data. So I'm looking for a working mapper from this to the correct decodable JSON or anything like that. – Mojtaba Hosseini Aug 12 '19 at 14:48
  • @MojtabaHosseini sorry, forgot about removing `\r\n`. Please, check now – Gralex Aug 12 '19 at 21:11
  • It's working with the responseStr you provided. But same error with the original one that I posted They are slightly different. – Mojtaba Hosseini Aug 12 '19 at 21:20
  • @MojtabaHosseini can you please give file example of response? As I mentioned `\"` converts to `"`, end there no way to distinguish. – Gralex Aug 13 '19 at 04:53
  • Absolutely. [Here is a fresh one](https://gist.github.com/MojtabaHs/a1f2207cac2432044dec641d1fbba2ab). This is the original response. – Mojtaba Hosseini Aug 13 '19 at 05:46
  • @MojtabaHosseini added you SO.txt to resources in playground. Works as expected. Read code from resources updated. – Gralex Aug 13 '19 at 08:08
  • It seem ok +1, let me check in the real situation and I will accept it then. You are vary hard worker. Thanks a lot. – Mojtaba Hosseini Aug 13 '19 at 09:37
  • { "title_properties": { "title": "

    20% off test

    ". I have json like this but i can't decode. I think the issue is with "". Can any one help
    – arunm Jun 02 '21 at 12:52