Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSON Parsing in Swift 3

Has anyone been able to find a way to parse through JSON files in Swift 3? I have been able to get the data to return but I am unsuccessful when it comes to breaking the data down into specific fields. I would post sample code but I've gone through so many different methods unsuccessfully and haven't saved any. The basic format I want to parse through is something like this. Thanks in advance.

{
  "Language": {

    "Field":[
          {
          "Number":"976",
          "Name":"Test"
          },
          {
          "Number":"977",
          "Name":"Test"
          }
       ]
   }
}
like image 925
Nas5296 Avatar asked Jul 02 '16 00:07

Nas5296


People also ask

How do you parse JSON in Swift?

This answer was last revised for Swift 5.3 and iOS 14.4 SDK. Given some already obtained JSON data, you can use JSONDecoder to decode it into your Decodable model (or a collection of models). Such model must conform to the Decodable protocol and contain correct mapping between properties and JSON dictionary keys.

How do I use JSON in Swift?

The first step to convert a JSON object to a Swift type is to create a model. For our example above, here's a struct we can use: As you can see, each JSON key will be represented by the property in the model above. Be sure to conform to the Codable protocol so it can be used to decode and encode JSON.

What is JSON used for in Swift?

JSON stands for JavaScript Object Notation. It's a popular text-based data format used everywhere for representing structured data. Almost every programming language supports it with Swift being no exception. You are going to use JSON a lot throughout your career, so make sure you don't miss out.


3 Answers

Have you tried JSONSerialization.jsonObject(with:options:)?

var jsonString = "{" +     "\"Language\": {" +     "\"Field\":[" +     "{" +     "\"Number\":\"976\"," +     "\"Name\":\"Test\"" +     "}," +     "{" +     "\"Number\":\"977\"," +     "\"Name\":\"Test\"" +     "}" +     "]" +     "}" +     "}"  var data = jsonString.data(using: .utf8)!  let json = try? JSONSerialization.jsonObject(with: data) 

Swift sometimes produces some very odd syntax.

if let number = json?["Language"]??["Field"]??[0]?["Number"] as? String {     print(number) } 

Everything in the JSON object hierarchy ends up getting wrapped as an optional (ie. AnyObject?). Array<T> subscript returns a non-optional T. For this JSON, which is wrapped in an optional, array subscript returns Optional<AnyObject>. However, Dictionary<K, V> subscript returns an Optional<V>. For this JSON, subscript returns the very odd looking Optional<Optional<AnyObject>> (ie. AnyObject??).

  • json is an Optional<AnyObject>.
  • json?["Language"] returns an Optional<Optional<AnyObject>>.
  • json?["Language"]??["Field"] returns an Optional<Optional<AnyObject>>.
  • json?["Language"]??["Field"]??[0] returns an Optional<AnyObject>.
  • json?["Language"]??["Field"]??[0]?["Number"] returns an Optional<Optional<AnyObject>>.
  • json?["Language"]??["Field"]??[0]?["Number"] as? String returns an Optional<String>.

The Optional<String> is then used by the if let syntax to product a String.


Final note: iterating the field array looks like this.

for field in json?["Language"]??["Field"] as? [AnyObject] ?? [] {     if let number = field["Number"] as? String {         print(number)     } } 

Swift 4 Update

Swift 4 makes this all much easier to deal with. Again we will start with your test data (""" makes this so much nicer).

let data = """ {   "Language": {      "Field":[           {           "Number":"976",           "Name":"Test"           },           {           "Number":"977",           "Name":"Test"           }        ]    } } """.data(using: .utf8)! 

Next we can define classes around the objects used in your JSON.

struct Object: Decodable {     let language: Language     enum CodingKeys: String, CodingKey { case language="Language" } }  struct Language: Decodable {     let fields: [Field]     enum CodingKeys: String, CodingKey { case fields="Field" } }  struct Field: Decodable {     let number: String     let name: String     enum CodingKeys: String, CodingKey { case number="Number"; case name="Name" } } 

The CodingKeys enum is how struct properties are mapped to JSON object member strings. This mapping is done automagically by Decodable.


Parsing the JSON now is simple.

let object = try! JSONDecoder().decode(Object.self, from: data)  print(object.language.fields[0].name)  for field in object.language.fields {     print(field.number) } 
like image 64
Jeffery Thomas Avatar answered Oct 21 '22 11:10

Jeffery Thomas


In Xcode 8 and Swift 3 id now imports as Any rather than AnyObject

This means that JSONSerialization.jsonObject(with: data) returns Any. So you have to cast the json data to a specific type like [String:Any]. Same applies to the next fields down the json.

var jsonString = "{" +     "\"Language\": {" +     "\"Field\":[" +     "{" +     "\"Number\":\"976\"," +     "\"Name\":\"Test1\"" +     "}," +     "{" +     "\"Number\":\"977\"," +     "\"Name\":\"Test2\"" +     "}" +     "]" +     "}" + "}"  var data = jsonString.data(using: .utf8)! if let parsedData = try? JSONSerialization.jsonObject(with: data) as! [String:Any] {     let language = parsedData["Language"] as! [String:Any]     print(language)     let field = language["Field"] as! [[String:Any]]     let name = field[0]["Name"]!     print(name) // ==> Test1 } 

In practice you would probably want some specific field buried in the json. Lets assume it's the Name field of the first element of Field array. You can use a chain of unwraps like this to safely access the field:

var data = jsonString.data(using: .utf8)! if let json = try? JSONSerialization.jsonObject(with: data) as? [String:Any],     let language = json?["Language"] as? [String:Any],     let field = language["Field"] as? [[String:Any]],     let name = field[0]["Name"] as? String, field.count > 0 {     print(name) // ==> Test1 } else {     print("bad json - do some recovery") } 

Also you may want to check Apple's Swift Blog Working with JSON in Swift

like image 30
serg_zhd Avatar answered Oct 21 '22 12:10

serg_zhd


Shoving JSON into a string manually is a pita. Why don't you just put the JSON into a file and read that in?

Swift 3:

let bundle = Bundle(for: type(of: self))
    if let theURL = bundle.url(forResource: "response", withExtension: "json") {
        do {
            let data = try Data(contentsOf: theURL)
            if let parsedData = try? JSONSerialization.jsonObject(with: data) as! [String:Any] {
                grok(parsedData)
            }
        } catch {
            print(error)
        }
    }
like image 20
Gene De Lisa Avatar answered Oct 21 '22 12:10

Gene De Lisa