X Pahadi
X Pahadi

Reputation: 7443

JSON Complex Arrays in Swift

IS there a way to achieve this in Swift?

var z = [ //Error 1
    {
        "Name":{ //Error 2
            "First":"Tika",
            "Last":"Pahadi"
        },
        "City":"Berlin",
        "Country":"Germany"
    }
]

var c:String = z[0]["Name"]["First"] as String //Error 3 

I get a bunch of errors like:

  1. Cannot convert the expression's type Array to ArrayLiteralConvertible
  2. Consecutive elements must be separated by a semi-colon
  3. Type 'Int' doesnot conform to protocol 'StringLiteralConvertible'

Upvotes: 0

Views: 2143

Answers (2)

Rob
Rob

Reputation: 437381

If you're representing this structure in Swift, use square brackets for the dictionaries, as well as for the arrays. And don't forget to unwrap optionals:

let z = [
    [
        "Name":[
            "First":"Tika",
            "Last":"Pahadi"
        ],
        "City":"Berlin",
        "Country":"Germany"
    ]
]

if let name = z[0]["Name"] as? [String: String], let firstName = name["First"] {
    // use firstName here
}

But let's say you really received that JSON as a result of some network request with URLSession. You could then parse that with JSONSerialization:

do {
    if let object = try JSONSerialization.jsonObject(with: data) as? [[String: Any]],
        let name = object[0]["Name"] as? [String: String],
        let firstName = name["First"] {
            print(firstName)
    }
} catch {
    print(error)
}

Or better, in Swift 4, we'd use JSONDecoder:

struct Name: Codable {
    let first: String
    let last: String

    enum CodingKeys: String, CodingKey {  // mapping between JSON key names and our properties is needed if they're not the same (in this case, the capitalization is different)
        case first = "First"
        case last = "Last"
    }
}

struct Person: Codable {
    let name: Name
    let city: String
    let country: String

    enum CodingKeys: String, CodingKey {  // ditto
        case name = "Name"
        case city = "City"
        case country = "Country"
    }
}

do {
    let people = try JSONDecoder().decode([Person].self, from: data)   // parse array of `Person` objects
    print(people)
} catch {
    print(error)
}

Upvotes: 3

gnasher729
gnasher729

Reputation: 52530

Swift can't guess what types there are in your JSON array. It can't guess that your data is an array, it can't guess that the first array element is a dictionary, and it can't guess that the value under the key "Name" is a dictionary. And indeed, you don't know that they are because you can't control what the server sends you.

So when NSJSONSerialization returns an AnyObject? you need to cast it to an NSArray* (and better do some checking or your app will crash if it's not an NSArray), check if there are any objects in the array, cast the first element to an NSDictionary* (again with checking to avoid a crash if it's not an NSDictionary*) and so on.

Upvotes: 1

Related Questions