JSONserialization.JSONobject Performance in Swift

Swift 4 JSONSerialization.jsonObject

If you're using Swift 4, I might suggest JSONDecoder:

First, define the types to hold the parsed data:

struct ResponseObject: Codable {
let data: [NewsItem]
}

struct NewsItem: Codable {
let newsId: Int
let title: String
let newsDate: Date
let newsURL: URL
let shortDescription: String
let categoryID: Int
let categoryName: String
let coordinates: [Coordinate]

// because your json keys don't follow normal Swift naming convention, use CodingKeys to map these property names to JSON keys

enum CodingKeys: String, CodingKey {
case newsId = "news_id"
case title
case newsDate = "news_date"
case newsURL = "news_url"
case shortDescription = "short_description"
case categoryID = "category_id"
case categoryName = "category_name"
case coordinates = "latlng"
}
}

struct Coordinate: Codable {
let lat: String
let lng: String
}

And then you can parse it:

let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(formatter)
do {
let responseObject = try decoder.decode(ResponseObject.self, from: data)
print(responseObject.data)
} catch {
print(error)
}

Clearly, if your JSON is different, you might need to change your objects accordingly (e.g. it struck me odd that latlng was an array of coordinates). Also, you can define custom init(from:) methods if you want to convert some of these strings into numbers, but I'd rather fix the JSON, instead (why are latlng returning string values rather than numeric values).

For more information, see Encoding and Decoding Custom Types.


As an aside, I'd advise against this pattern (note, this is your network request logic, but excised of NSData):

let data = try! Data(contentsOf: url.getURL())

That will retrieve the data synchronously, which can be problematic because

  • the app will be frozen while the data is being retrieved resulting in a poor UX;
  • you risk having your app killed by the watchdog process which looks for frozen apps; and
  • you don't have robust error handling and this will crash if the network request fails.

I'd suggest using URLSession:

let task = URLSession.shared.dataTask(with: url.getURL()) { data, _, error in
guard let data = data, error == nil else {
print(error ?? "Unknown error")
return
}

// now parse `data` like shown above

// if you then need to update UI or model objects, dispatch that back
// to the main queue:

DispatchQueue.main.async {
// use `responseObject.data` to update model objects and/or UI here
}
}
task.resume()

Swift : JSONSerialization.jsonObject not working for json into json

Using as! [String:String] you are saying that all values in the dictionary will be of String type, but the nested JSON related to the state key is obviously not a String

use as? [String: Any] and then cast your other properties as needed. result["error"] as String

Using this method it makes getting the nested data more difficult than it needs to be:

if let state = result["state"] as? [String: String] {
let name = state["name"]
let value = state["value"]
}

Notes

  1. When using Swift 4 or greater you should be using the Codable Protocol
  2. You should not be force unwrapping, use conditional unwrapping (as? [String: Any])

EDIT:

An example of how you can do this using Codable and how it is used.

Playground code

// you dont need this part, I am not making network request
let jsonData = """
{"error":"true","message":"no","state":{"id":"1","name":"empty"}}
""".data(using: .utf8)

struct ErrorState: Codable
{
let id: String
let name: String
}

struct ErrorResponse: Codable
{
let error: String
let message: String
let state: ErrorState
}

guard let data = jsonData else { fatalError() }

let errorResponse = try? JSONDecoder().decode(ErrorResponse.self, from: data)
print(errorResponse?.state.name)

Swift JSONSerialization.jsonObject Error

The jsonObject function will return a value of type Any but the jsonArray's type of NSMutableArray. And this function will throw an error if something is wrong, put a try keyword before it. In my experience, let change the type of jsonArray to array of dictionary, so you will extract data with ease.

    do {
let data: Data = try Data(contentsOf: url as URL)
let jsonArray: [[String: AnyObject]] = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! [[String: AnyObject]]
print("json: \(jsonArray)")

for dict in jsonArray {
let dataName = dict["dataName"] as! String
print("dataName: \(dataName)")
}
}
catch {
print("Error: (data: contentsOf: url)")
}

Swift - JSONSerialization invalid JSON

As @RobNapier pointed out I needed FHIRModels, from Apple. I'm able to get data more easily than using a messy Codable data approach manually.

import ModelsR4

let resource = try decoder.decode(Immunization.self, from: data)

print output is proper

28581000087106
28581000087106

Getting value from JSONSerialization key Swift

Forget JSONSerialization and use Decodable with JSONDecoder:

struct DataModel: Decodable {
let id: String
let pedId: String?
let ownerId: String?
let locationId: String?
let status: String
}
do {
let dataModel = try JSONDecoder().decode(DataModel.self, from: data)
print("Status: \(dataModel.status)")
} catch ...

If you want to use JSONSerialization, note that status is not a dictionary, it's a String:

if let dictionary = jsonData as? [String: Any] {
if let status = dictionary["status"] as? String {
print("Status: \(status)")
}
}



Related Topics



Leave a reply



Submit