-
Notifications
You must be signed in to change notification settings - Fork 73
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
Initialise one object from different JSON sources. #29
Comments
... but I guess unless we have really generic protocols and not just the |
This is a legitimate concert however. I recently encountered a similar need, when a backend service exposes the same entity in 2 different formats (sucks, but it happens) |
I haven't had to deal with this myself yet. But doesn't skipping adopting the Decodable-protocol work fine? It may not be super beautiful but still: extension TheObject { // Not conforming to Decodable
func decode1(json: AnyObject) throws -> TheObject { ... }
func decode2 ... // But better function names
// Or only one function but with extra parameters as you did
// This could also be a curried function to get a closure to pass to the
// decode parameter in -parse<T>
func decode(json: AnyObject, type: AnEnumOrSomething)
}
// Either
let json = ["a" : ["b": ["Some": "Thing"]]]
let a = TheObject.decode1(json => "a" => "b")
// or
let a = parse(json, ["a", "b"], decode: TheObject.decode2) Or one could potentially start overloading the func parse(json: AnyObject, path: [String], type: something) throws -> TheObject edit forgot path argument in last example |
I think the problem with the different decode functions, that you want the type information to "trickle down" from parent to child objects (so that they know as well with what kind/type of json they are dealing with). Not sure if it's anywhere clear what I mean by that ;-), but I guess in essence it would mean, that using your On the other hand I think you are right that one could work around the issue in a less "beautiful" way. Currently I am achieving a similar things by having different overloaded initialisers like |
Oh, now I think I see what you did in your example. You leveraged the type-system to determine the decode-function at compile-time, and thats why you use struct-types instead of enums? But doesn't that work excellent? The first |
Understood the toughness you spoke of when trying to replicate this in a playground. It's a really cool idea/problem though. And perhaps it could be solved in another way. Keeping this open :) |
I think this would be a nice approach of handling it, but doesn't work now import UIKit
import Decodable
// We can't include operator overloads in protocol-extensions but we can do this, just that it doesn't work either.
// Imagine every operator overload was using DynamicDecodable instead, and the Decodable-protocol extends that protocol
protocol DynamicDecodable {
static var decodeClosure: (json: AnyObject) throws -> Self {get}
}
// Temporary hack to avoid ambiguity
infix operator =>> { associativity right precedence 150 }
func =>> <T: DynamicDecodable>(lhs: AnyObject, rhs: String) throws -> T {
// toJSONPathArray is private, only one key
return try parse(lhs, path: [rhs], decode: T.decodeClosure)
}
// One of our new custom protocols
protocol ServerDecodable {
static func fromServerJSON(json: AnyObject) throws -> Self
}
extension ServerDecodable {
static var decodeClosure: (json: AnyObject) throws -> Self {
return fromServerJSON
}
}
// Models
struct User: ServerDecodable {
let login: String
static func fromServerJSON(json: AnyObject) throws -> User {
return try User(login: json => "login")
}
}
struct Repository: ServerDecodable {
let name: String
let user: User
static func fromServerJSON(json: AnyObject) throws -> Repository {
// Error: Argument for generic parameter 'T' could not be inferred
return try Repository(name: "test", user: json =>> "user")
}
} Edit: I'm stupid |
Edit: I have no idea what I was talking about here, obviously the decode function I gave would need to take an additional parameter in my example. DELETED |
If Decodable wants to solve this problem, it may be an idea to explore adding an 'or' operator. I'll do some exploration, but it would look like this at the call site:
|
Thinking about the problem further, I don't think it's going to be possible for my above syntax to work, as the => operator will either throw, or it won't, and I don't think we can have an operator that intercepts this throw and tries again. Another syntax, although not as nice, and definitely more 'magic' feeling would be to have a nested array for alternate key names, thusly:
The nested array is because Decodable supports an array for a path, so a top level array of alternates isn't going to work, specifying a path and alternate keys might look like |
From an implementation standpoint, my above suggestion seems ... rather complex, bordering on unmaintainable/untenable/impractical. I started an exploration into what would be required, and it involves changing the path string type to be an AnyObject type and include type-checking with an asymmetric recursive traversal to cover possible use cases. I've actually done something like this for some natural language processing as well as chord shape generation, and I really wouldn't wish the maintenance of this sort of thing on anyone. So, uh ... never mind. I think the real solution to this lies in #95 / 1, which is seems our dear Viking friend is in the midst of! |
What about having a context parameter in the decode function? |
Say we have an object, which should be initialised from a JSON coming over the network and from a different JSON e.g. coming from the disk. This is currently not possible since, the
decode
function has no way of knowing the "type" of the JSON it is getting.Adding type information to the
Decodable
protocol would allow for having differentdecode
functions to work with the same object:The text was updated successfully, but these errors were encountered: