Hi Alejandro, > On Nov 6, 2017, at 3:14 PM, Alejandro Martinez via swift-evolution > <[email protected]> wrote: > > I’m in favor of this as it’s really, but specially the custom strategy. And > thinking about that, would there be a way to expose the functionality of > converting a string to camel case or snake case so it could be used in case > of writing a custom strategy. Would be cool to have the original > functionality when writings a custom strategy instead of having to > reimplement it if there is a need. > > Sent from my iPad
We talked about this a bit, and while there are some clever things that are possible, ultimately we felt it made the API too complex. I think we have room to provide the functionality on the enum later if we want (e.g. a function you could call). - Tony > > On 6 Nov 2017, at 20:54, Tony Parker via swift-evolution > <[email protected] <mailto:[email protected]>> wrote: > >> Hi everyone, >> >> While we have no formal process at this time for proposals of changes to >> Foundation-only code, I would still like to post one that we have run >> through our internal process here for additional public comment. >> >> Link to PR with proposal content: >> >> https://github.com/apple/swift-corelibs-foundation/pull/1301 >> <https://github.com/apple/swift-corelibs-foundation/pull/1301> >> >> Link to implementation for the overlay: >> >> https://github.com/apple/swift/pull/12779 >> <https://github.com/apple/swift/pull/12779> >> >> Markdown follows. >> >> Thanks, >> - Tony >> >> # Key Strategies for JSONEncoder and JSONDecoder >> >> * Proposal: SCLF-0001 >> * Author(s): Tony Parker <[email protected] >> <mailto:[email protected]>> >> >> ##### Related radars or Swift bugs >> >> * <rdar://problem/33019707 <rdar://problem/33019707>> Snake case / Camel >> case conversions for JSONEncoder/Decoder >> >> ##### Revision history >> >> * **v1** Initial version >> >> ## Introduction >> >> While early feedback for `JSONEncoder` and `JSONDecoder` has been very >> positive, many developers have told us that they would appreciate a >> convenience for converting between `snake_case_keys` and `camelCaseKeys` >> without having to manually specify the key values for all types. >> >> ## Proposed solution >> >> `JSONEncoder` and `JSONDecoder` will gain new strategy properties to allow >> for conversion of keys during encoding and decoding. >> >> ```swift >> class JSONDecoder { >> /// The strategy to use for automatically changing the value of keys >> before decoding. >> public enum KeyDecodingStrategy { >> /// Use the keys specified by each type. This is the default >> strategy. >> case useDefaultKeys >> >> /// Convert from "snake_case_keys" to "camelCaseKeys" before >> attempting to match a key with the one specified by each type. >> /// >> /// The conversion to upper case uses `Locale.system`, also known as >> the ICU "root" locale. This means the result is consistent regardless of the >> current user's locale and language preferences. >> /// >> /// Converting from snake case to camel case: >> /// 1. Capitalizes the word starting after each `_` >> /// 2. Removes all `_` >> /// 3. Preserves starting and ending `_` (as these are often used to >> indicate private variables or other metadata). >> /// For example, `one_two_three` becomes `oneTwoThree`. >> `_one_two_three_` becomes `_oneTwoThree_`. >> /// >> /// - Note: Using a key decoding strategy has a nominal performance >> cost, as each string key has to be inspected for the `_` character. >> case convertFromSnakeCase >> >> /// Provide a custom conversion from the key in the encoded JSON to >> the keys specified by the decoded types. >> /// The full path to the current decoding position is provided for >> context (in case you need to locate this key within the payload). The >> returned key is used in place of the last component in the coding path >> before decoding. >> case custom(([CodingKey]) -> CodingKey) >> } >> >> /// The strategy to use for decoding keys. Defaults to `.useDefaultKeys`. >> open var keyDecodingStrategy: KeyDecodingStrategy = .useDefaultKeys >> } >> >> class JSONEncoder { >> /// The strategy to use for automatically changing the value of keys >> before encoding. >> public enum KeyEncodingStrategy { >> /// Use the keys specified by each type. This is the default >> strategy. >> case useDefaultKeys >> >> /// Convert from "camelCaseKeys" to "snake_case_keys" before writing >> a key to JSON payload. >> /// >> /// Capital characters are determined by testing membership in >> `CharacterSet.uppercaseLetters` and `CharacterSet.lowercaseLetters` (Unicode >> General Categories Lu and Lt). >> /// The conversion to lower case uses `Locale.system`, also known as >> the ICU "root" locale. This means the result is consistent regardless of the >> current user's locale and language preferences. >> /// >> /// Converting from camel case to snake case: >> /// 1. Splits words at the boundary of lower-case to upper-case >> /// 2. Inserts `_` between words >> /// 3. Lowercases the entire string >> /// 4. Preserves starting and ending `_`. >> /// >> /// For example, `oneTwoThree` becomes `one_two_three`. >> `_oneTwoThree_` becomes `_one_two_three_`. >> /// >> /// - Note: Using a key encoding strategy has a nominal performance >> cost, as each string key has to be converted. >> case convertToSnakeCase >> >> /// Provide a custom conversion to the key in the encoded JSON from >> the keys specified by the encoded types. >> /// The full path to the current encoding position is provided for >> context (in case you need to locate this key within the payload). The >> returned key is used in place of the last component in the coding path >> before encoding. >> case custom(([CodingKey]) -> CodingKey) >> } >> >> >> /// The strategy to use for encoding keys. Defaults to `.useDefaultKeys`. >> open var keyEncodingStrategy: KeyEncodingStrategy = .useDefaultKeys >> } >> ``` >> >> ## Detailed design >> >> The strategy enum allows developers to pick from common actions of >> converting to and from `snake_case` to the Swift-standard `camelCase`. The >> implementation is intentionally simple, because we want to make the rules >> predictable. >> >> Converting from snake case to camel case: >> >> 1. Capitalizes the word starting after each `_` >> 2. Removes all `_` >> 3. Preserves starting and ending `_` (as these are often used to indicate >> private variables or other metadata). >> >> For example, `one_two_three` becomes `oneTwoThree`. `_one_two_three_` >> becomes `_oneTwoThree_`. >> >> Converting from camel case to snake case: >> >> 1. Splits words at the boundary of lower-case to upper-case >> 2. Inserts `_` between words >> 3. Lowercases the entire string >> 4. Preserves starting and ending `_`. >> >> For example, `oneTwoThree` becomes `one_two_three`. `_oneTwoThree_` becomes >> `_one_two_three_`. >> >> We also provide a `custom` action for both encoding and decoding to allow >> for maximum flexibility if the built-in options are not sufficient. >> >> ## Example >> >> Given this JSON: >> >> ``` >> { "hello_world" : 3, "goodbye_cruel_world" : 10, "key" : 42 } >> ``` >> >> Previously, you would customize your `Decodable` type with custom keys, like >> this: >> >> ```swift >> struct Thing : Decodable { >> >> let helloWorld : Int >> let goodbyeCruelWorld: Int >> let key: Int >> >> private enum CodingKeys : CodingKey { >> case helloWorld = "hello_world" >> case goodbyeCruelWorld = "goodbye_cruel_world" >> case key >> } >> } >> >> var decoder = JSONDecoder() >> let result = try! decoder.decode(Thing.self, from: data) >> ``` >> >> With this change, you can write much less boilerplate: >> >> ```swift >> struct Thing : Decodable { >> >> let helloWorld : Int >> let goodbyeCruelWorld: Int >> let key: Int >> } >> >> var decoder = JSONDecoder() >> decoder.keyDecodingStrategy = .convertFromSnakeCase >> let result = try! decoder.decode(Thing.self, from: data) >> ``` >> >> ## Alternatives considered >> >> None. >> >> _______________________________________________ >> swift-evolution mailing list >> [email protected] <mailto:[email protected]> >> https://lists.swift.org/mailman/listinfo/swift-evolution >> <https://lists.swift.org/mailman/listinfo/swift-evolution> > _______________________________________________ > swift-evolution mailing list > [email protected] > https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
