tomatentobi
tomatentobi

Reputation: 3157

Best practice to find out if SwiftyJSON dictionary has key

I'm trying to find a way to get the value of a key in a SwiftyJSON dictionary and return a default string, if the key is not set. The following example works fine, but I'm not really satisfied. Do you know a more elegant way?

Example:

let users: JSON = [
    ["id": 1, "name": "one"],
    ["id": 2],
    ["id": 3, "name": "three"]
]
    
for (key: String, user: JSON) in users {      
    println(user.object.objectForKey("name") != nil
        ? user["name"].stringValue
        : "default")
}

Upvotes: 23

Views: 18891

Answers (6)

ciauri
ciauri

Reputation: 513

The latest version of SwiftyJSON has the exists() function.

NOTE: Their latest documentation does not reflect the code very well... The actual method is exists()

// Returns boolean

json["name"].exists()

Upvotes: 28

Eneko Alonso
Eneko Alonso

Reputation: 19682

If you need to check if a SwiftyJSON dictionary has a key, you can compare it with JSON.null as follows:

user["name"] == JSON.null // true if the key does not exist

Using that method, your code could look like this:

let users: JSON = [
    ["id": 1, "name": "one"],
    ["id": 2],
    ["id": 3, "name": "three"]
]

for (key, user) in users {
    print(user["name"] != JSON.null ? user["name"].stringValue : "default")
}

If you just want to provide a default value, then you can use the Nil Coalescing Operator in Swift (??) like this:

let users: JSON = [
    ["id": 1, "name": "one"],
    ["id": 2],
    ["id": 3, "name": "three"]
]

for (key, user) in users {
    print(user["name"].string ?? "default")
}

SwiftJSON provides the string method, which returns an optional value (String?), contrary to stringValue which always returns a String.

Upvotes: 7

Brian Tracy
Brian Tracy

Reputation: 6831

Note: This answer is no longer valid

Seems like the perfect use of if let syntax. Because accessing the subscript will return an optional, bind it to a variable in an if let block. This will tell you wether or not the key was in the JSON.

if let name = user["name"] {    // returns optional
    // user["name"] was non-nil, is now stored in ``name""
    // do something with ``name""
} 
else {
    // user did not have key "name"
    // here would be the place to return "default"
    // or do any other error correction if the key did not exist
}

Upvotes: -1

Mazyod
Mazyod

Reputation: 22559

As far as I can tell, this works:

let users: JSON = [
    ["id": 1, "name": "one"],
    ["id": 2],
    ["id": 3, "name": "three"]
]

for jsonDict in users.arrayValue {      
    println(jsonDict["name"].string ?? "default")
}

To explain, you want to cast the JSON object to the types you expect to get. If you want the type to always succeed (not optional), use xxxValue. Hence, we cast the root object using arrayValue.

arrayValue returns [JSON], which we traverse in the loop. We don't need to case the jsonDict: JSON to object, as SwiftyJSON is smart enough to assume it is a dict from the subscript operators.

Finally, trying to fetch a key from the dict, then checking if the key can be retrieved as a string using .string, which is an optional. Use null coalescing to assign a default value.

Please see the SwiftJSON doc for string and stringValue

Upvotes: 0

Ash
Ash

Reputation: 9351

Objects of type JSON have a calculated property called null. This returns NSNull if the element requested does not exist, nil if it does. You can therefore test for the existence of a specific object by extracting it from its parent JSON and testing to see if the null property exists. I personally handle it like this:

let object = json["key"]
if object.null == nil {
    // Do something with 'object'
} else {
    // Key does not exist - handle the error
}

In your specific case, you could use this:

for (key: String, user: JSON) in users {
    let name = user.object.objectForKey("name")
    println(name.null == nil ? name.stringValue : "default")
}

...but since we know we're looking for a string we can cut that down even further. All you actually need to do is drop the 'Value' part of stringValue, since string will return nil if it cannot generate a string from its content. Also, check out the nil coalescence operator ??. It's designed to provide a default for optional variables, so try this:

for (key: String, user: JSON) in users {
    println(user["name"].string ?? "default")
}

Nice and compact!

Upvotes: 1

rintaro
rintaro

Reputation: 51911

It seems, SwiftyJSON sets the error property when subscript non-existing key.

So, this should works:

for (key: String, user: JSON) in users {
    let name = user["name"];
    println(name.error == nil ? name.stringValue : "default")
}

For example: w/ Version 6.1.1 (6A2006), SwiftyJSON github current master:

let users: JSON = [
    ["id": 1, "name": "one"],
    ["id": 2],
    ["id": 3, "name": NSNull()],
    ["id": 4, "name": "four"],
]

for (key: String, user: JSON) in users {
    let name = user["name"];
    name.isEmpty
    println(name.error == nil ? name.stringValue : "default")
}

prints:

one
default

four

Upvotes: 9

Related Questions