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

Reply via email to