thoughtbot/Argo

Decode "Any" and decoding keys arbitrarily

ftuckey-chwy opened this issue · 8 comments

I need to decode the following JSON, its keys could be anything

"DeviceStateVariables": {
                          "FanMode": "Auto",
                          "Mode": "CoolOn",
                          "Heating": 68,
                          "Cooling": 67,
                          "Temperature": 67
                          "AnyKey": AnyValue of AnyType
                        }

to get StateVariable objects like:

class StateVariable {
	type: String
	value: Any
}
StateVariable(type:"FanMode",value:"Auto")
StateVariable(type:"Mode",value:"CoolOn")
StateVariable(type:"Heating",value:68)
StateVariable(type:"Cooling",value:67)
StateVariable(type:"Temperature",value:67)

is this possible? AFAIK it is not possible to decode to Any and ecoding keys arbitrarily also isn't really well supported

Argo Version

Argo 3.0.0_

Hey @fedtuck, I think the easiest thing to do here would be to decode to [String: String] then map over that to get your [StateVariables]. Something like

let variables: Decoded<[String: String]> = json <| "DeviceStateVariables"
let states Decoded<[StateVariables]> = variables.map { vars in
  vars.map { item in
    let (key, value) = item
    return StateVariable(type: key, value: value)
  }
}

or you could fancy that up a bit to get

let variables: Decoded<[String: String]> = json <| "DeviceStateVariables"
let states Decoded<[StateVariables]> = variables.map { $0.map { StateVariable(type: $0.0, value: $0.1) } }

I think that should do it.

@tonyd256 thanks a lot for your help. The issue Im still having is that DeviceStateVariables is actually [String:Any], so I cannot use Decoded<[String: String]. Do you know any workaround for that?

If you conform Any to Decodable that should do the trick. That would mostly involve simply returning the value from all the cases of JSON in the decode function.

@tonyd256 Im trying to implement this, but the line

let variables: Decoded<[String: String]> = json <| "DeviceStateVariables"

doesn't work for me. I get an error
screen shot 2017-01-05 at 00 01 11

Do you happen to know what could it be?

hmm, this (

static func decode(j: JSON) -> Decoded<[String: Value]> {
) should allow you to do that ... I'll try it out in a playground tomorrow and get back to you.

We don't have a definition of <| that works with dictionaries. You'll need to do use decodeObject instead:

let variables: Decoded<[String: String]> = (json <| "DeviceStateVariables").flatMap(decodeObject)

That should work for you.

@fedtuck any update here? Did that end up doing the trick?

I'm going to go ahead and close this due to inactivity but please feel free to reopen if this is still an issue.