JSON encoding with backslashes
Asked Answered
R

6

10

I m using Alamofire and SwiftyJSOn to parse JSON output. It works very well however some sites give json with escaped output. I use Alamofire like below

    Alamofire.request(.POST, url, parameters: param, encoding: .JSON)
        .responseJSON { (req, res, json, error) in
         var json = JSON(json!)

Site gives me JSON result with escaped string so SwiftyJSON can't decode it. How can I convert below

{
    "d": "{\"UniqeView\":{\"ArrivalDate\":null,\"ArrivalUnitId\":null,\"DeliveryCityName\":null,\"DeliveryTownName\":null},\"ErrorMessage\":null,\"Message\":null,\"IsFound\":false,\"IsSuccess\":true}"
}

to something like

{
       "d": {
          "UniqeView": {
             "ArrivalDate": null,
             "ArrivalUnitId": null,
             "DeliveryCityName": null,
             "DeliveryTownName": null
          },
          "ErrorMessage": null,
          "Message": null,
          "IsFound": false,
          "IsSuccess": true
       }
    }
Renferd answered 23/3, 2015 at 16:0 Comment(0)
Z
6
// This Dropbox url is a link to your JSON
// I'm using NSData because testing in Playground
if let data = NSData(contentsOfURL: NSURL(string: "https://www.dropbox.com/s/9ycsy0pq2iwgy0e/test.json?dl=1")!) {

    var error: NSError?
    var response: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.allZeros, error: &error)
    if let dict = response as? NSDictionary {
        if let key = dict["d"] as? String {

            let strData = key.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
            var error: NSError?
            var response: AnyObject? = NSJSONSerialization.JSONObjectWithData(strData!, options: NSJSONReadingOptions.allZeros, error: &error)

            if let decoded = response as? NSDictionary {
                println(decoded["IsSuccess"]!)   // => 1
            }

        }
    }
}

I guess you have to decode twice: the wrapping object, and its content.

Zellner answered 23/3, 2015 at 16:42 Comment(1)
Thanks for your answer. Even though I use Alamofire, your comments helped me to solve the issue.Renferd
R
5

@ericd comments helped me to solve the issue. I accepted his answer for this question. Since I am using Alamofire for asynchronous operation, and SwiftyJSON, I couldn't use his code. Here is the code with Alamofire and SwiftyJSON.

Alamofire.request(.POST, url, parameters: param, encoding: .JSON)
   .responseJSON { (req, res, json, error) in
    if(error != nil) {
        NSLog("Error: \(error)")
        failure(res, json, error)
    }
    else {

        var jsond = JSON(json!)
        var data = jsond["d"].stringValue.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
        jsond = JSON(data: data!)
Renferd answered 24/3, 2015 at 10:19 Comment(1)
When i use .data(using: .utf8, allowLossyConversion: false) it sends something like "24 bytes". How did you manage to get it to work?Reamy
G
2

Lots of people have problems distinguishing between what they get and what their system prints. So first step is you need to find out what exactly you are receiving, and whether or not these escape characters are just an artefact of you printing.

If this is what you actually receive, then the server has sent you a dictionary with a single key "d" and a string, and the string contains serialized data. In that case, convert the string to NSData and shove it into NSJSONSerialization, which will turn it into the dictionary that you want. This is a rather stupid way to transmit JSON data, but it happens.

Grouping answered 23/3, 2015 at 16:31 Comment(0)
F
1

Here is another approach for Swift 4 - Using Codable

This was the json that I received:

{
    "error_code": 0,
    "result": {
        "responseData": "{\"emeter\":{\"get_realtime\":{\"voltage_mv\":237846,\"current_ma\":81,\"power_mw\":7428,\"total_wh\":1920,\"err_code\":0}}}"
    }
} 

The JSON part with backslashes is equal to this:

{
    "emeter": {
        "get_realtime": {
            "voltage_mv": 237846,
            "current_ma": 81,
            "power_mw": 7428,
            "total_wh":19201,
            "err_code":0
        }
    }
}

And this was the code that I used:

import Foundation

class RealtimeEnergy: Codable {
    let errorCode: Int
    let result: ResultRealtimeEnergy?
    let msg: String?

    enum CodingKeys: String, CodingKey {
        case errorCode = "error_code"
        case result, msg
    }

    init(errorCode: Int, result: ResultRealtimeEnergy?, msg: String?) {
        self.errorCode = errorCode
        self.result = result
        self.msg = msg
    }
}

class ResultRealtimeEnergy: Codable {

    let responseData: String
    var emeter: Emeter

    enum CodingKeys: String, CodingKey {
        case responseData
    }

    required init(from decoder: Decoder) throws {

        let container = try decoder.container(keyedBy: CodingKeys.self)
        responseData = try container.decode(String.self, forKey: .responseData)
        let dataString = try container.decode(String.self, forKey: .responseData)
        emeter = try JSONDecoder().decode(Emeter.self, from: Data(dataString.utf8))
    }
}



class Emeter: Codable {
    let emeter: EmeterClass

    init(emeter: EmeterClass) {
        self.emeter = emeter
    }
}

class EmeterClass: Codable {
    let getRealtime: GetRealtime

    enum CodingKeys: String, CodingKey {
        case getRealtime = "get_realtime"
    }

    init(getRealtime: GetRealtime) {
        self.getRealtime = getRealtime
    }
}

class GetRealtime: Codable {
    let voltageMv, currentMa, powerMw, totalWh: Int
    let errCode: Int

    enum CodingKeys: String, CodingKey {
        case voltageMv = "voltage_mv"
        case currentMa = "current_ma"
        case powerMw = "power_mw"
        case totalWh = "total_wh"
        case errCode = "err_code"
    }

    init(voltageMv: Int, currentMa: Int, powerMw: Int, totalWh: Int, errCode: Int) {
        self.voltageMv = voltageMv
        self.currentMa = currentMa
        self.powerMw = powerMw
        self.totalWh = totalWh
        self.errCode = errCode
    }
}

And this is the trick:

emeter = try JSONDecoder().decode(Emeter.self, from: Data(dataString.utf8))
Forest answered 1/5, 2019 at 12:35 Comment(0)
R
0

i use some custom function to do this work:

import Foundation

func unescapeString(string: String) -> String {
    return string.stringByReplacingOccurrencesOfString("\"", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
}

I hope it helps ;)

Rabbit answered 23/3, 2015 at 16:25 Comment(2)
it helped me for a specific problem where serializing actually returning nilConciliar
This is actually what I neededCule
C
0

I spent way too long trying to figure out the same issue. Here's how I solved it.

I have a network manager that when called, returns a response of [Any]?

I loop through each record, converting it to JSON, but that doesn't recognize the inherent dictionary structure in this case.

So I pluck the rawString and then use parse. This does recognize the dictionary.

From there you should be able to use it as you would. In my example, I pass the data to a data model (MyApi),

networkManager .requestResource(withUrl: urlPath, andParams: params, 
successHandler: { (response: [Any]?) in

            if let rawResponse = response {

                let mutableArray = NSMutableArray()

                for item in rawResponse {

                    let jsonData = JSON(item)
                    guard let rawString = jsonData.rawString() else {
                        return
                    }

                    let parsedData = JSON.parse(rawString)

                let typedResponse = MyApi(json: parsedData)
                    mutableArray.add(typedResponse)
                }

                let array = mutableArray.copy() as! [MyApi]
                //do something with array

            } else {
                let error = NSError .init(domain: "MyApi", code: 100, userInfo: nil)
                //log error
            }

        }, failureHandler: { (response: [Any]?) in

            let error = NSError .init(domain: "MyApi", code: 101, userInfo: nil)
            //log error
        })
Chopper answered 9/7, 2017 at 21:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.