[swift-users] Decode a JSON object of unknown format into a Dictionary with Decodable in Swift 4

Tony Parker anthony.parker at apple.com
Thu Jun 22 18:10:11 CDT 2017


Hi Jon,

Usually this boils down to a question of: what are you going to do with the Any?

If you intended to cast it to a dictionary and get at its values using string keys, then writing a struct with the properties you care about is the way we recommend doing this. You don’t have to include every possible key.

There are some more dynamic behaviors possible with the Any, but the point of this API was to try to move a lot of the parsing into a place where the compiler could help you not only to generate the boilerplate but catch errors at compile time.

The omission of an API to just get the rest of the data as some kind of Any is both for reasons of abstraction (not every data format is JSON, and the Any truly could be anything in other formats), and a statement of intent of how we believe this API is best used.

- Tony

> On Jun 18, 2017, at 7:23 PM, Jon Shier via swift-users <swift-users at swift.org> wrote:
> 
> Given that, per his description, “metadata” can be anything, creating a struct doesn’t really help.
> 
> 
> 
> Jon Shier
> 
>> On Jun 18, 2017, at 9:00 PM, somu subscribe <somu.subscribe at gmail.com <mailto:somu.subscribe at gmail.com>> wrote:
>> 
>> Create a struct for Metadata and conform to Coding
>> 
>> Code:
>> 
>> let string = """
>> {
>>   "id": "4yq6txdpfadhbaqnwp3",
>>   "email": "john.doe at example.com <mailto:john.doe at example.com>",
>>   "name":"John Doe",
>>   "metadata": {
>>     "link_id": "linked-id",
>>     "buy_count": 4
>>   }
>> }
>> """
>> 
>> let data = string.data(using: .utf8)! //Force unwrapping just for ease of explanation, use guard instead
>> 
>> struct User: Codable {
>>     
>>     //Struct to handle the nested JSON
>>     struct Metadata : Codable {
>>         var linkID   : String
>>         var buyCount : Int
>>         
>>         //Customisation, if you want if you want your properties to be different from the JSON key
>>         private enum CodingKeys : String, CodingKey {
>>             case linkID     = "link_id"
>>             case buyCount   = "buy_count"
>>         }
>>     }
>>     
>>     var id: String
>>     var email    : String
>>     var name     : String
>>     var metadata : Metadata
>> }
>> 
>> let decoder = JSONDecoder()
>> 
>> do {
>>     let user = try decoder.decode(User.self, from: data)
>>     print(user)
>> }
>> catch {
>>     print("error:\(error)")
>> }
>> 
>> Reference:
>> 
>> https://developer.apple.com/videos/play/wwdc2017/212/ <https://developer.apple.com/videos/play/wwdc2017/212/>
>> 
>> 
>> Regards,
>> Muthu
>> 
>> 
>> 
>>> On 19 Jun 2017, at 3:29 AM, Jon Shier via swift-users <swift-users at swift.org <mailto:swift-users at swift.org>> wrote:
>>> 
>>> 	The more I use Codable, the less suited to networking it becomes. In reading a variety of blog posts about implementing custom Decodable support from JSON, I kept running across the same pattern. Basically, users had started implementing their own decoding protocols which wrap Decodable types, and have a type that represents the JSON representation and then their real type, with an initializer connecting the two. But apparently this is Apple’s official solution, which is rather terrible since it would be completely unnecessary if the Decodable APIs were more flexible or we could access keys by key path rather than nesting full containers. I can’t image how much code I would have to add to decode the nasty JSON APIs I’ve used Argo to parse before. Every type would need an underlying raw representation that, at the very least, would need custom keys, lest I pollute even those models with the terrible keys the JSON actually has. Not to mention the various transforms I needed. Once I hit any reasonably complex API, Argo is far far simpler to implement in fewer lines of code.
>>> 	In trying to make Argo’s JSON enum Decodable itself, I can’t seem to find a way to access the Any representation of the raw JSON. In fact, it appears there’s no way to represent an Any value in Codable at all, which makes Codable rather useless for the scenarios like the one that prompted this thread. Without such an ability it’s impossible to actually use Codable with all of the JSON out there, where other solutions work just fine. Argo’s JSON type is decodable by Argo, so you can use it to represent a blob of JSON just fine. Other existing JSON frameworks have similar solutions. 
>>> 
>>> 
>>> 
>>> Jon
>>> 
>>>> On Jun 18, 2017, at 3:21 AM, Rien via swift-users <swift-users at swift.org <mailto:swift-users at swift.org>> wrote:
>>>> 
>>>> Dang, hit send too soon. Sorry.
>>>> 
>>>> This does not address your question, so please ignore… (foot in mouth)!
>>>> 
>>>> Regards,
>>>> Rien
>>>> 
>>>> Site: http://balancingrock.nl <http://balancingrock.nl/>
>>>> Blog: http://swiftrien.blogspot.com <http://swiftrien.blogspot.com/>
>>>> Github: http://github.com/Balancingrock <http://github.com/Balancingrock>
>>>> Project: http://swiftfire.nl <http://swiftfire.nl/> - An HTTP(S) web server framework in Swift
>>>> 
>>>> 
>>>> 
>>>> 
>>>> 
>>>> 
>>>> 
>>>>> On 18 Jun 2017, at 09:19, Rien <Rien at Balancingrock.nl <mailto:Rien at Balancingrock.nl>> wrote:
>>>>> 
>>>>> Are you looking for a general purpose JSON interpreter / API ?
>>>>> 
>>>>> There are many of them around, and in fact I do have my own: https://github.com/Balancingrock/VJson <https://github.com/Balancingrock/VJson>
>>>>> 
>>>>> With VJson I would write:
>>>>> 
>>>>> let json = VJson.parse(… your json object…)
>>>>> 
>>>>> and then access the metadata as:
>>>>> 
>>>>> let buyCount = (json | ”metadata” | ”buy_count”)?.intValue
>>>>> 
>>>>> or:
>>>>> 
>>>>> var buyCount: Int &= json | “metadata” | “buy_count”
>>>>> 
>>>>> To loop over the content of metadata:
>>>>> 
>>>>> for item in (json | “metadata”) ?? [ ] {
>>>>> 	print (item.nameValue)
>>>>> 	switch item.type {
>>>>> 	case .object: …
>>>>> 	case .number: …
>>>>> 	case .string: …
>>>>> 	etc...
>>>>> 	}
>>>>> }
>>>>> 
>>>>> Obviously I am plugging my own code here, but there are many others around, I understand that SwiftyJSON is quite popular but I have not used that myself.
>>>>> 
>>>>> Regards,
>>>>> Rien
>>>>> 
>>>>> Site: http://balancingrock.nl <http://balancingrock.nl/>
>>>>> Blog: http://swiftrien.blogspot.com <http://swiftrien.blogspot.com/>
>>>>> Github: http://github.com/Balancingrock <http://github.com/Balancingrock>
>>>>> Project: http://swiftfire.nl <http://swiftfire.nl/> - An HTTP(S) web server framework in Swift
>>>>> 
>>>>> 
>>>>> 
>>>>> 
>>>>> 
>>>>> 
>>>>> 
>>>>>> On 18 Jun 2017, at 04:07, Chris Anderson via swift-users <swift-users at swift.org <mailto:swift-users at swift.org>> wrote:
>>>>>> 
>>>>>> Say I have a JSON object such as:
>>>>>> 
>>>>>> {
>>>>>>  "id": "4yq6txdpfadhbaqnwp3",
>>>>>>  "email": "john.doe at example.com <mailto:john.doe at example.com>",
>>>>>>  "name":"John Doe",
>>>>>>  "metadata": {
>>>>>>    "link_id": "linked-id",
>>>>>>    "buy_count": 4
>>>>>>  }
>>>>>> }
>>>>>> 
>>>>>> And with a struct of:
>>>>>> 
>>>>>> struct User: Codable {
>>>>>> var id: String
>>>>>> var email: String
>>>>>> var name: String
>>>>>> }
>>>>>> 
>>>>>> How can I decode the `metadata` field into a Dictionary?
>>>>>> 
>>>>>> I’ve tried doing things such as, in my struct,
>>>>>> 
>>>>>> var metadata: Dictionary
>>>>>> 
>>>>>> or
>>>>>> 
>>>>>> var metadata: [String: Any]
>>>>>> 
>>>>>> But I get the error 
>>>>>> 
>>>>>> MyPlayground.playground:3:7: note: cannot automatically synthesize 'Encodable' because '<<error type>>' does not conform to 'Encodable'
>>>>>> var metadata: Dictionary 
>>>>>> 
>>>>>> A meta or metadata field on many APIs (such as www.stripe.com <http://www.stripe.com/>) can contain whatever you want, and I still want to be able to process it on the Swift end. How can I store that meta data field into a Dictionary that I can parse apart manually after?
>>>>>> 
>>>>>> Thanks!
>>>>>> 
>>>>>> Chris Anderson
>>>>>> 
>>>>>> 	
>>>>>> 
>>>>>> 
>>>>>> 
>>>>>> _______________________________________________
>>>>>> swift-users mailing list
>>>>>> swift-users at swift.org <mailto:swift-users at swift.org>
>>>>>> https://lists.swift.org/mailman/listinfo/swift-users <https://lists.swift.org/mailman/listinfo/swift-users>
>>>>> 
>>>> 
>>>> _______________________________________________
>>>> swift-users mailing list
>>>> swift-users at swift.org <mailto:swift-users at swift.org>
>>>> https://lists.swift.org/mailman/listinfo/swift-users <https://lists.swift.org/mailman/listinfo/swift-users>
>>> 
>>> _______________________________________________
>>> swift-users mailing list
>>> swift-users at swift.org <mailto:swift-users at swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-users <https://lists.swift.org/mailman/listinfo/swift-users>
>> 
> 
> _______________________________________________
> swift-users mailing list
> swift-users at swift.org
> https://lists.swift.org/mailman/listinfo/swift-users

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20170622/4f1e0579/attachment.html>


More information about the swift-users mailing list