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

Reply via email to