Serialize JSON string that contains escaped (backslash and double quote) Swift return Badly formed object

Jefferson Setiawan picture Jefferson Setiawan · May 16, 2018 · Viewed 7.7k times · Source

I have response string from the backend like this:

{
    "status": "success",
    "data": "{\"name\":\"asd\",\"address\":\"Street 1st\"}"
}

I think the problem was because the double quote (") in the data String. When I remove the double quote, the serialization was success. But the response is from backend and I have to deal with it.

Anyone can solve this problem?

Thank you.

Here is the playground code.

import Foundation
var jsonStr = """
{
"status": "success",
"data": "{\"name\":\"asd\",\"address\":\"Street 1st\"}"
}
"""
let data = jsonStr.data(using: .utf8)
if let d = data {
    do {
        let o = try JSONSerialization.jsonObject(with: d)
        print(o)
    } catch let e {
        print(e)
    }
} else {
    print("DATA conversion ERROR")
}

Answer

vadian picture vadian · May 16, 2018

First of all if you wrap the JSON in the literal string syntax of Swift 4 you have to escape the backslashes.

let jsonStr = """
{
"status": "success",
"data": "{\\"name\\":\\"asd\\",\\"address\\":\\"Street 1st\\"}"
}
"""

You got nested JSON. The value for key data is another JSON string which must be deserialized separately

let jsonData = Data(jsonStr.utf8)

do {
    if let object = try JSONSerialization.jsonObject(with: jsonData) as? [String:String] {
        print(object)
        if let dataString = object["data"] as? String {
            let dataStringData = Data(dataString.utf8)
            let dataObject = try JSONSerialization.jsonObject(with: dataStringData) as? [String:String]
            print(dataObject)
        }
    }
} catch {
    print(error)
}

Or – with a bit more effort but – much more comfortable with the (De)Codable protocol

struct Response : Decodable {

    private enum CodingKeys : String, CodingKey { case status, data }

    let status : String
    let person : Person

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        status = try container.decode(String.self, forKey: .status)
        let dataString = try container.decode(String.self, forKey: .data)
        person = try JSONDecoder().decode(Person.self, from: Data(dataString.utf8))
    }
}

struct Person : Decodable {
    let name, address : String
}

let jsonStr = """
{
"status": "success",
"data": "{\\"name\\":\\"asd\\",\\"address\\":\\"Street 1st\\"}"
}
"""
let jsonData = Data(jsonStr.utf8)

do {
    let result = try JSONDecoder().decode(Response.self, from: jsonData)
    print(result)
} catch {
    print(error)
}