Re: [swift-users] Is there any way to decode from Any type JSON Object using JSONDecoder?
(Added swift users as CC) 2017-06-24 3:47 GMT+09:00 Masaki Haga <hgmsk1...@gmail.com>: > Hi Tony, > > Got it. Thank you very much for taking time for this. > > 2017-06-24 1:40 GMT+09:00 Tony Parker <anthony.par...@apple.com>: > >> Hi Masaki, >> >> Thanks for the additional info. I have a slightly different idea on how >> to approach this which I think will be more performant. Let me work on that >> and get back to you. >> >> - Tony >> >> >> On Jun 22, 2017, at 7:51 PM, Masaki Haga <hgmsk1...@gmail.com> wrote: >> >> Hi Tony, >> >> Thank you very much for replying. Let me clarify what I was saying. >> >> Let’s say, we have a JSON like this: >> >> { >> "badge": 1, >> "content_available": false, >> "mutable_content": false >> } >> >> and to decode this JSON, we have to have a Codable model like below with >> custom CodingKeys enum: >> >> struct APS: Codable { >> enum CodingKeys: String, CodingKey { >> case badge = "badge" >> case contentAvailable = "content_available" >> case mutableContent = "mutable_content" >> } >> >> var badge: Int >> var contentAvailable: Bool >> var mutableContent: Bool >> } >> >> This is a very small JSON so it is not hard to write this custom enum. >> However, it is very cumbersome to define this enum in all of the >> pre-existing models in our project to be able to decoded by Decodable and >> we rather prefer just to have a model something like this: >> >> struct APS: Codable { >> var badge: Int >> var contentAvailable: Bool >> var mutableContent: Bool >> } >> >> To be able to decode this model with JSONDecoder, I wrote a JSON >> converter like this: >> >> extension String { >> var camelcased: String { >> return self >> .components(separatedBy: "_") >> .enumerated() >> .map { 0 == $0.offset ? $0.element : $0.element.capitalized } >> .joined() >> } >> } >> // This extension above was referenced from an article written in >> Japanese: http://qiita.com/takasek/items/77955948fe283758ee55 >> >> struct JSONCaseConverter { >> public static func process(_ JSONObject: Any) -> Any { >> if var dict = JSONObject as? [String: Any] { >> for (key, value) in dict { >> dict[key.camelcased] = process(value) >> dict.removeValue(forKey: key) >> } >> return dict >> } else if let array = JSONObject as? [Any] { >> return array.map(process) >> } else { >> return JSONObject >> } >> } >> } >> >> Basically, this JSONCaseConverter go though all the keys in a JSON and >> convert the key from snake-case to camel-case so that the JSON can be >> decoded directly with the model without custom CodingKeys enum. >> >> And then if we have a Data type JSON object (typically got from >> URLSession.dataTask) and want to do some processing like this and decode >> with JSONDecoder, we need to do: >> >> 1. Serialize Data object with JSONSerialization.jsonObject(with:) and >> get Any type JSON Object >> 2. do some processing to the Any type JSON Object >> 3. Serialize Any type Object with JSONSerialization.data(withJSONObject:) >> and get Data type JSON Object back. >> 4. and then call JSONDecoder.decode(). >> >> However, JSONSerialization.jsonObject(with:) is called again in >> JSONDecoder.decode() implementation so there is a computational redundancy. >> >> Because I have already seen several this camel-case vs snake-case >> discussion in some places including Swift Evolution, I guess not a small >> number of developers will take the similar apploach ( I understand >> automatic key renaming could be a unsafe operation and this is just my >> personal opinion). >> >> Anyways, I was wondering if there is any way to opt-out the >> JSONSerialization.jsonObject(with:) in JSONDecoder.decode(). And if not, >> is it a good idea to have one more API such as `decode(_ type: T.Type, >> from JSONObject: Any)` which I think gives more flexibility to the API? >> >> Regards, >> - Masaki >> >> 2017-06-23 8:01 GMT+09:00 Tony Parker <anthony.par...@apple.com>: >> >>> Hi Masaki, >>> >
[swift-users] Is there any way to decode from Any type JSON Object using JSONDecoder?
Hi Swift-Users, I was wondering if there is any way to decode JSON from Any type JSON Object using `JSONDecoder`, not from Data type object. Currently, `JSONDecoder` has only one decode function which decodes Data type object to `Decodable`. Inside the function, it serializes Data object to Any Type JSON Object using `JSONSerialization` and pass it into `_JSONDecoder(referencing:, options:)` (Refer JSONEncoder.swift#874). As discussed in some of other threads such as "SE-0166: Swift Archival & Serialization", the default implementation of JSONDecoder or Decodable protocol doesn’t allow to decode from one format to another format (such as snake-case to camel-case), we need to implement custom CodingKey enums. However, in our project, to parse the server API JSON response with snake-case, declaring custom CodingKey enums for all the pre-existing models is almost impossible and very inefficient, so I decided to covert all the JSON keys from snake-case to camel-case, and then pass it into `JSONDecoder.decode`. To achieve this, we need to convert the Data object resulted from `URLSession.task` to Any type JSON Object using `JSONSerialization`, do the conversion from snake-case to camel-case and then convert back to Data type and then pass to `JSONDecoder.decode` which looks very redundant because the function uses `JSONSerialization` inside of it as mentioned above. If there is a function like below, we can get rid of this redundant call of `JSONSerialization`. ``` func decode(_ type: T.Type, from JSONObject: Any) throws -> T ``` Sorry if I am misunderstanding the new API but is there any way to decode `Decodable` directly from Any type JSON Object? If not, I think adding the function aforementioned and giving an ability to opt-out this JSON serialization call would give more flexibility to the API in my humble opinion. Thank you for reading. All the best, - Masaki ___ swift-users mailing list swift-users@swift.org https://lists.swift.org/mailman/listinfo/swift-users
[swift-users] Is there any way to decode from Any type JSON Object using JSONDecoder?
Hi Swift-Users, I was wondering if there is any way to decode JSON from Any type JSON Object using `JSONDecoder`, not from Data type object. Currently, `JSONDecoder` has only one decode function which decodes Data type object to `Decodable`. Inside the function, it serializes Data object to Any Type JSON Object using `JSONSerialization` and pass it into `_JSONDecoder(referencing:, options:)` (Refer JSONEncoder.swift#874). As discussed in some of other threads such as "SE-0166: Swift Archival & Serialization", the default implementation of JSONDecoder or Decodable protocol doesn’t allow to decode from one format to another format (such as snake-case to camel-case), we need to implement custom CodingKey enums. However, in our project, to parse the server API JSON response with snake-case, declaring custom CodingKey enums for all the pre-existing models is almost impossible and very inefficient, so I decided to covert all the JSON keys from snake-case to camel-case, and then pass it into `JSONDecoder.decode`. To achieve this, we need to convert the Data object resulted from `URLSession.task` to Any type JSON Object using `JSONSerialization`, do the conversion from snake-case to camel-case and then convert back to Data type and then pass to `JSONDecoder.decode` which looks very redundant because the function uses `JSONSerialization` inside of it as mentioned above. If there is a function like below, we can get rid of this redundant call of `JSONSerialization`. ``` func decode(_ type: T.Type, from JSONObject: Any) throws -> T ``` Sorry if I am misunderstanding the new API but is there any way to decode `Decodable` directly from Any type JSON Object? If not, I think adding the function aforementioned and giving an ability to opt-out this JSON serialization call would give more flexibility to the API in my humble opinion. Thank you for reading. All the best, - Masaki ___ swift-users mailing list swift-users@swift.org https://lists.swift.org/mailman/listinfo/swift-users