Reputation: 599
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!!!
Upvotes: 0
Views: 216
Reputation: 299425
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
}
}
Upvotes: 0
Reputation: 285140
feed
is not valid JSON. You have to replace the single quotes with double quotes.Data
object from the string (casting the type doesn't work).Side note:
Don't use NS...
collection types and NSString
in Swift.
Upvotes: 1
Reputation: 24341
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)
}
Upvotes: 0