+1. Would allow me to delete a ton of code in my current project.

> On Nov 6, 2017, at 2:54 PM, 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 
> <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 <anthony.par...@apple.com 
> <mailto:anthony.par...@apple.com>>
> 
> ##### 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
> 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