0

I am trying to parse the json. But the problem is I have json inside to it another json String. Like :

{
    "count": 284,
    "next": "http://X:X:X:X:8080/api/sensor/last5feed?page=2&search=XXXXX",
    "previous": null,
    "results": [
        {
            "id": 571,
            "feed": "{'app_id': 'XXXXX', 'dev_id': 'XXXX', 'hardware_serial': 'XXXXX', 'port': 6, 'counter': 4290, 'payload_raw': 'AQEBIwXsF4IAAAA=', 'payload_fields': {'aamsg_type': 'weather', 'abstatus': 0, 'batteryV': 3.5, 'battery_low': 'no', 'humiP': 60.2, 'tempC': 15.2}, 'metadata': {'time': '2020-01-23T15:09:32.350967362Z', 'frequency': 868.1, 'modulation': 'LORA', 'data_rate': 'SF7BW125', 'airtime': 61696000, 'coding_rate': '4/5', 'gateways': [{'gtw_id': 'XXXXX', 'timestamp': 3227230963, 'time': '2020-01-23T15:09:32.326146Z', 'channel': 0, 'rssi': -98, 'snr': 4.8, 'rf_chain': 1, 'latitude': 57.124737, 'longitude': -2.1646452, 'altitude': 90, 'location_source': 'registry'}]}}",
            "created_at": "2020-01-23T15:09:32.630326Z",
            "sensor": 1
        },
        {
            "id": 569,
            "feed": "{'app_id': 'XXXXXX', 'dev_id': 'XXXX', 'hardware_serial': 'XXXX', 'port': 6, 'counter': 4289, 'payload_raw': 'XXXXX', 'payload_fields': {'aamsg_type': 'weather', 'abstatus': 0, 'batteryV': 3.5, 'battery_low': 'no', 'humiP': 57.6, 'tempC': 16.9}, 'metadata': {'time': '2020-01-23T14:09:32.132070865Z', 'frequency': 867.3, 'modulation': 'LORA', 'data_rate': 'SF7BW125', 'airtime': 61696000, 'coding_rate': '4/5', 'gateways': [{'gtw_id': 'XXXXXX', 'timestamp': 3921981659, 'time': '2020-01-23T14:09:32.104672Z', 'channel': 4, 'rssi': -107, 'snr': 8.2, 'rf_chain': 0, 'latitude': 57.124737, 'longitude': -2.1646452, 'altitude': 90, 'location_source': 'registry'}]}}",
            "created_at": "2020-01-23T14:09:32.448929Z",
            "sensor": 1
        }
}

I am getting the values till feed. But I am not able to parse further, My code is :

if(status_code == 200){
  if let json = response.data {
    do{
      let data = try JSON(data: json)
      let result = data["results"].arrayObject! as NSArray
      let ct = result.count
       if(ct != 0 ) {
       self.noDataFound.isHidden = true
       for i in 0...ct-1 {
          let data = result[i] as? NSDictionary
          let feed = data?.value(forKey: "feed") as? NSString
          let data3 = try JSON(data: feed as! Data) . 
          print(data3)
        }
    }
} catch {} }}

I need to get the hardware_serial from feed. Can any body please help me what i am doing wrong here!! Thanks!!!

5
  • use Codable to parse json Commented Feb 3, 2020 at 10:45
  • Can you please write code to do so ! Thanks Commented Feb 3, 2020 at 10:46
  • @Piyush Please check your JSON. This is not a valid JSON. Commented Feb 3, 2020 at 11:20
  • @Rob thanks for the info. I have solved that. Commented Feb 3, 2020 at 12:13
  • Did you manage to parse a JSON string, or did you change JSON format? No suggested solution seems to work. Commented May 10, 2022 at 12:18

3 Answers 3

1
  1. The string for key feed is not valid JSON. You have to replace the single quotes with double quotes.
  2. Create a Data object from the string (casting the type doesn't work).
  3. Create a JSON object from the data.
  4. Get the values you need.

Side note:

Don't use NS... collection types and NSString in Swift.

Sign up to request clarification or add additional context in comments.

Comments

0

Use Codable to parse the above JSON response.

Models:

struct Root: Codable {
    let count: Int
    let next: String
    let previous: String?
    let results: [Result]
}

struct Result: Codable {
    let id: Int
    let feed, createdAt: String
    let sensor: Int
}

Parse the JSON data like so,

do {
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    let response = try decoder.decode(Root.self, from: data)
    print(response)
} catch {
    print(error)
}

Comments

0

First, peel off the piece you want:

struct ResultWrapper: Decodable {
    let results: [Result]
}

And then build a custom decoder to extract feed, which is malformed. Single-quotes are not legal JSON. As a hack, the following code just substitutes all single-quotes with double-quotes, but this won't work if there are any embedded quoted-single-quotes.

struct Result: Decodable {
    enum CodingKeys: String, CodingKey { case feed }
    let feed: Feed
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        // inner JSON is mal-formed. Have to fix it.
        let feedString = try container.decode(String.self, forKey: .feed)
            .replacingOccurrences(of: "'", with: "\"")

        feed = try JSONDecoder().decode(Feed.self, from: Data(feedString.utf8))
    }
}

Finally, decoding Feed is mechanical, but requires custom CodingKeys. I recommend quicktype for that:

// https://app.quicktype.io?share=7O6iNJ3ugXb4mr84TIoG
struct Feed: Codable {
    var appID, devID, hardwareSerial: String
    var port, counter: Int
    var payloadRaw: String
    var payloadFields: PayloadFields
    var metadata: Metadata

    enum CodingKeys: String, CodingKey {
        case appID = "app_id"
        case devID = "dev_id"
        case hardwareSerial = "hardware_serial"
        case port, counter
        case payloadRaw = "payload_raw"
        case payloadFields = "payload_fields"
        case metadata
    }
}

// MARK: - Metadata
struct Metadata: Codable {
    var time: String
    var frequency: Double
    var modulation, dataRate: String
    var airtime: Int
    var codingRate: String
    var gateways: [Gateway]

    enum CodingKeys: String, CodingKey {
        case time, frequency, modulation
        case dataRate = "data_rate"
        case airtime
        case codingRate = "coding_rate"
        case gateways
    }
}

// MARK: - Gateway
struct Gateway: Codable {
    var gtwID: String
    var timestamp: Int
    var time: String
    var channel, rssi: Int
    var snr: Double
    var rfChain: Int
    var latitude, longitude: Double
    var altitude: Int
    var locationSource: String

    enum CodingKeys: String, CodingKey {
        case gtwID = "gtw_id"
        case timestamp, time, channel, rssi, snr
        case rfChain = "rf_chain"
        case latitude, longitude, altitude
        case locationSource = "location_source"
    }
}

// MARK: - PayloadFields
struct PayloadFields: Codable {
    var aamsgType: String
    var abstatus: Int
    var batteryV: Double
    var batteryLow: String
    var humiP, tempC: Double

    enum CodingKeys: String, CodingKey {
        case aamsgType = "aamsg_type"
        case abstatus, batteryV
        case batteryLow = "battery_low"
        case humiP, tempC
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.