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