bignerdranch / Freddy

A reusable framework for parsing JSON in Swift.
MIT License
1.09k stars 120 forks source link

[Help] Mapping a single element when an array is expected #237

Closed edasque closed 7 years ago

edasque commented 7 years ago

Hi,

I have a call that typically returns an array of dictionaries which I am able to map into an array of structure (via getArray followed by a map). When there is just one result though, the call returns a simple dictionary for that one result (instead of an array of one dictionary). This is a third party API so I cannot change this behavior.

While I can get the keys and values with the dictionary, I can't figure out a way to map it using the same structure init function that I wrote for the array.

Ideally I'd want to do the following: 1) Figure out whether at:"query","results","quote" is an array or a dictionary 2) Based on that use getArray.map or map automatically the dictionary into element and return it as [element] a one item array.

Thank you in advance,

Erik

mdmathias commented 7 years ago

One way to handle this is to make use of the error JSON.Error.valueNotConvertible that Freddy provides. Below, data could be whatever you pull down from your server API. The Resident type comes from Freddy's test suite.

func getDataThatMaybeIsArrayOrDictionary() {
//        let data = [ ["name": "Matt", "age": 33, "hasPet": false, "rent": 1000.0],
//                                 ["name": "Drew", "age": 34, "hasPet": true, "rent": 1234.5] ] as JSON
    let data = ["name": "Pat", "age": 40, "hasPet": false, "rent": 111.11] as JSON

    do {
        let residents = try data.decodedArray(type: Resident.self)
        print("It's an array: \(residents)")
    } catch let JSON.Error.valueNotConvertible(value, type) where type == Array<JSON>.self {
        do {
            let pat = try value.decode(type: Resident.self)
            print("It's a dictionary: \(pat)")
        } catch {
            // Handle error
        }
    } catch {
        // Handle error
    }
}

The nested do/catch is not ideal. You could do something like this instead if dealing with an optional makes sense:

func getDataThatMaybeIsArrayOrDictionary() {
//        let data = [ ["name": "Matt", "age": 33, "hasPet": false, "rent": 1000.0],
//                                 ["name": "Drew", "age": 34, "hasPet": true, "rent": 1234.5] ] as JSON
    let data = ["name": "Pat", "age": 40, "hasPet": false, "rent": 111.11] as JSON

    do {
        let residents = try data.decodedArray(type: Resident.self)
        print("It's an array: \(residents)")
    } catch let JSON.Error.valueNotConvertible(value, type) where type == Array<JSON>.self {
-        do {
-            let pat = try value.decode(type: Resident.self)
-            print("It's a dictionary: \(pat)")
-        } catch {
-            // Handle error
-        }
+       guard let pat = try? value.decode(type: Resident.self) else { return }
+       print("It's a dictionary: \(pat)")
    } catch {
        // Handle error
    }
}
edasque commented 7 years ago

thank you, I'll try that.