Yeha the API surface is a good concern. I was wondering if it would make sense to be part of String for example instead of tying it to the encoders api. I guess add that point it will be a different discussion. In any case, good proposal ;)
Sent from my iPad > On 7 Nov 2017, at 00:35, Tony Parker <anthony.par...@apple.com> wrote: > > Hi Alejandro, > >> On Nov 6, 2017, at 3:14 PM, Alejandro Martinez via swift-evolution >> <swift-evolution@swift.org> 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 >>> <swift-evolution@swift.org> 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 >>> >>> Link to implementation for the overlay: >>> >>> https://github.com/apple/swift/pull/12779 >>> >>> Markdown follows. >>> >>> Thanks, >>> - Tony >>> >>> # Key Strategies for JSONEncoder and JSONDecoder >>> >>> * Proposal: SCLF-0001 >>> * Author(s): Tony Parker <anthony.par...@apple.com> >>> >>> ##### Related radars or Swift bugs >>> >>> * <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 >>> swift-evolution@swift.org >>> https://lists.swift.org/mailman/listinfo/swift-evolution >> _______________________________________________ >> swift-evolution mailing list >> swift-evolution@swift.org >> https://lists.swift.org/mailman/listinfo/swift-evolution >
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution