Best practice to find out if SwiftyJSON dictionary has key
Asked Answered
I

6

23

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")
}
Illustration answered 16/12, 2014 at 23:5 Comment(3)
did you ever find a solution to that? thanks!Excerpt
@Hannes: no, unfortunately notIllustration
I think there are better JSON libraries out there nowadays, in most use cases ObjectMapper works a lot better since it'll create objects instead of having to slug through all kinds of stringly typed optional dictionary items.Oliverolivera
P
28

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()
Perkin answered 15/4, 2016 at 20:56 Comment(3)
At least in version 2.3.3 and version 3 of SwiftyJSON that method is still isExists()Detection
SwiftyJSON (3.1.3) uses .exists() syntax.Chauffeur
Not work for me, print true even when value is null.Ensanguine
W
9

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
Wednesday answered 17/12, 2014 at 3:3 Comment(3)
I also tried this way, but it directly crashes after let name = user["name"];.Illustration
At least, it works in my environment, as added to the answer. Please provides more info, what error?Wednesday
Your version (imgur.com/ZDiyRds) works until the second user. Then it can't find the getter (imgur.com/GgjiEUA). Do you use a different master? github.com/SwiftyJSON/SwiftyJSON/blob/master/Source/…Illustration
T
7

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.

Turaco answered 23/12, 2015 at 17:38 Comment(0)
S
1

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!

Stringendo answered 1/7, 2015 at 6:16 Comment(1)
Oops, just realized my answer is almost the same as the last part you wrote. Well, for easier access, I guess I'll leave the answerSusurrus
S
0

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

Susurrus answered 10/9, 2015 at 10:27 Comment(0)
C
-1

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
}
Casebook answered 18/12, 2014 at 5:22 Comment(1)
No, this is invalid: i.imgur.com/ST0MgLX.png. Making a cast as JSON crashes too: i.imgur.com/wpwlI6H.pngIllustration

© 2022 - 2024 — McMap. All rights reserved.