Your gist is extremely interesting to me. I had tried something similar 
with Argo’s existing JSON enum, but was somewhat stymied by trying to decode it 
from an unkeyed container. I suppose I still don’t have a good grasp on all of 
the existing container types.



Jon


> On Jun 23, 2017, at 11:46 AM, Randy Eckenrode via swift-users 
> <swift-users@swift.org> wrote:
> 
>> 
>> On Jun 17, 2017, at 10:07 PM, Chris Anderson via swift-users 
>> <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:
>> 
>> Say I have a JSON object such as:
>> 
>>   {
>>     "id": "4yq6txdpfadhbaqnwp3",
>>     "email": "john....@example.com <mailto:john....@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?
> 
> It’s possible, but you have to do most of the work yourself because you the 
> compiler can’t create implementations for you. See below for a possible 
> implementation. Note that I just ignore types I don’t handle. I also took a 
> stab at doing something general in this gist 
> (https://gist.github.com/kenada/069e121371eb8db41231edfcd4bd14a8 
> <https://gist.github.com/kenada/069e121371eb8db41231edfcd4bd14a8>), but it 
> doesn’t implement very robust error handling or support encoding. It also 
> doesn’t flatten down to Any/[Any]/[String: Any] (leaving it up to the user to 
> destructure the enum).
> 
> import Foundation
> 
> struct User: Codable {
>     var id: String
>     var email: String
>     var name: String
>     var metadata: [String: Any]
> 
>     init(from decoder: Decoder) throws {
>         let container = try decoder.container(keyedBy: StaticCodingKeys.self)
>         self.id = try container.decode(String.self, forKey: .id)
>         self.email = try container.decode(String.self, forKey: .email)
>         self.name = try container.decode(String.self, forKey: .name)
>         self.metadata = try User.decodeMetadata(from: 
> container.superDecoder(forKey: .metadata))
>     }
> 
>     func encode(to encoder: Encoder) throws {
>         var container = encoder.container(keyedBy: StaticCodingKeys.self)
>         try container.encode(self.id, forKey: .id)
>         try container.encode(self.email, forKey: .email)
>         try container.encode(self.name, forKey: .name)
>         try encodeMetadata(to: container.superEncoder(forKey: .metadata))
>     }
> 
>    static func decodeMetadata(from decoder: Decoder) throws -> [String: Any] {
>         let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
>         var result: [String: Any] = [:]
>         for key in container.allKeys {
>             if let double = try? container.decode(Double.self, forKey: key) {
>                 result[key.stringValue] = double
>             } else if let string = try? container.decode(String.self, forKey: 
> key) {
>                 result[key.stringValue] = string
>             }
>         }
>         return result
>     }
> 
>     func encodeMetadata(to encoder: Encoder) throws {
>         var container = encoder.container(keyedBy: DynamicCodingKeys.self)
>         for (key, value) in metadata {
>             switch value {
>             case let double as Double:
>                 try container.encode(double, forKey: 
> DynamicCodingKeys(stringValue: key)!)
>             case let string as String:
>                 try container.encode(string, forKey: 
> DynamicCodingKeys(stringValue: key)!)
>             default:
>                 fatalError("unexpected type")
>             }
>         }
>     }
> 
>     private enum StaticCodingKeys: String, CodingKey {
>         case id, email, name, metadata
>     }
> 
>     private struct DynamicCodingKeys: CodingKey {
>         var stringValue: String
> 
>         init?(stringValue: String) {
>             self.stringValue = stringValue
>         }
> 
>         var intValue: Int?
> 
>         init?(intValue: Int) {
>             self.init(stringValue: "")
>             self.intValue = intValue
>         }
>     }
> }
> 
> let userJson = """
>   {
>     "id": "4yq6txdpfadhbaqnwp3",
>     "email": "john....@example.com <mailto:john....@example.com>",
>     "name":"John Doe",
>     "metadata": {
>       "link_id": "linked-id",
>       "buy_count": 4
>     }
>   }
> """.data(using: .utf8)!
> 
> let decoder = JSONDecoder()
> let user = try! decoder.decode(User.self, from: userJson)
> print(user)
> // Prints: User(id: "4yq6txdpfadhbaqnwp3", email: "john....@example.com 
> <mailto:john....@example.com>", name: "John Doe", metadata: ["buy_count": 
> 4.0, "link_id": "linked-id"])
> 
> let encoder = JSONEncoder()
> let data = try! encoder.encode(user)
> print(String(data: data, encoding: .utf8)!)
> // Prints: {"email":"john....@example.com 
> <mailto:john....@example.com>","id":"4yq6txdpfadhbaqnwp3","metadata":{"link_id":"linked-id","buy_count":4},"name":"John
>  Doe"}
> 
> -- 
> Randy Eckenrode
> _______________________________________________
> swift-users mailing list
> swift-users@swift.org <mailto:swift-users@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-users 
> <https://lists.swift.org/mailman/listinfo/swift-users>
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Reply via email to