Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[String: [DecodableObject]] #334

Closed
ligerjohn opened this issue Mar 25, 2016 · 5 comments
Closed

[String: [DecodableObject]] #334

ligerjohn opened this issue Mar 25, 2016 · 5 comments
Labels

Comments

@ligerjohn
Copy link

I am trying to decode an object with arrays (JSON) into Swift, making it a Dictionary with String keys and Array values. I was able to solve it in the end, but found the solution quite inelegant compared to "normal" use of Argo. I'm hoping to get some examples of doing this more elegantly or if there aren't I'm hoping Argo could be extended to make it elegant.

The JSON structure would be something along the lines of this. All fake names to protect the innocent:

"thing": {
    "variable": {
        "one": [],
        "two": [],
        "three": [],
        
    }
}

So this was my first, naïve try. It would be nice if this syntax worked. i.e. if Dictionary were treated as a Decodable the same way Array etc are.

struct Thing {
    var variable: [String: [OtherThing]] // OtherThing is Decodable
}

extension Thing: Decodable {
    static func decode(j: JSON) -> Decoded<Thing> {
        return curry(Thing.init)
            <^> j <| "variable"
    }
}

Searching through the issues and documentation I proceeded to make this change. It worked if the dictionary did not have an Array as value, but again fails to compile if there's an array as value.

<^> (j <| "variable" >>- [String: [OtherThing]].decode)

This is the final version I ended up with. decodeVariable(j: JSON) does work (unless I made some typos here) but it's quite inelegant and messy compared to the common case.

struct Thing {
    var variable: [String: [OtherThing]] // OtherThing is Decodable
}

extension Thing: Decodable {
    static func decode(j: JSON) -> Decoded<Thing> {
        return curry(Thing.init)
            <^> decodeVariable(j)
    }
}

private static func decodeVariable(j: JSON) -> Decoded<[String: [OtherThing]]> {
        if let variableJson = decodedJSON(j, forKey: "variable").value,
            let variable: [String: JSON] = [String: JSON].decode(variableJson).value {

                var variableInternal = [String: [OtherThing]]()
                for (key, value) in variable {
                    if let array = [OtherThing].decode(value).value as [OtherThing]? {
                        variableInternal[key] = array
                  } else {
                      return .Failure(.Custom("\(key) should contain an array"))
                    }
                }
                return .Success(variableInternal)
        }
        return .Failure(.MissingKey("variable"))
    }

I wanted to bring it up as even though I did solve the issue others might have similar issues and I couldn't find any info on it.

@gfontenot
Copy link
Collaborator

Ugh, yes. This is an issue that should disappear with Swift 3.0.

The basic issue here is that contextual types like Array and Dictionary (and Optional, etc) can't actually conform to Decodable the way value types like String can. One of the listed goals for Swift 3.0 is:

the ability to make a constrained extension conform to a new protocol (i.e., an array of Equatable elements is Equatable)

This would help us immensely, and would mean being able to greatly simplify our API. If we could say that arrays of Decodable objects are themselves Decodable, then we'd be able to kill the entire family of <|| operators, and your initial solution would Just Work.

I think what you've come up with is a reasonable solution to this unfortunate limitation, and I don't have any immediate suggestions of how else to solve it.

@ligerjohn
Copy link
Author

Sounds like Swift 3.0 can clean this up and would probably remove some further minor "issues". Mainly that we are using a fair amount of optionals, as well as NSDate, NSURL. I've gotten it all to work, but any time I can make something simpler I'm happy.

I'd be happy to have the ticket closed, I'm happy with the answer. Thanks for writing and open sourcing Argo, it's been quite useful.

@gfontenot
Copy link
Collaborator

fwiw, one of the points of Argo is that you shouldn't have to rely on optionals where they don't make sense. What issues have you run into around those (and NSDate, NSURL)?

@gfontenot
Copy link
Collaborator

Closing this but I'd still love to hear about my questions above if you have time, @ligerjohn.

@zhijunsheng
Copy link

@ligerjohn Is there any update on handling the similar tasks? Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants