Search code examples
iosjsonswiftnsjsonserializationjsonserializer

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


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")
}

Solution

  • 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)
    }