Hi Itai, I see. I thought that the second reason you mentioned is so reasonable. Apart from this proposal, if enum with String as RawValue is defined by omitting StringLiteral, it would be a good idea to have a mechanism to generate snake_case or a space delimited string.
2017-11-08 2:20 GMT+09:00 Itai Ferber <ifer...@apple.com>: > Hi Norio, > > There are two reasons that I think this is valuable over doing something > in CodingKeys: > > 1. The definition you give your coding keys affects all encoding > formats. JSON is a format where snake_case can be relatively common, so the > transformation makes a lot of sense there. For other formats, like plist > files or otherwise, the transformation might not make as much sense. > Instead of affecting all of your coding keys globally, this limits it to > JSON. > 2. More importantly, this allows you to transform keys of things which > you don’t necessarily own. If you’re working with types that you didn’t > write (but which are expected to have snake_case keys nonetheless), this > allows you to perform that transformation. If this were instead an > annotation on CodingKeys directly, you wouldn’t be able to perform it > on types you don’t directly own. > > — Itai > > On 6 Nov 2017, at 17:39, Norio Nomura via swift-evolution wrote: > > Hi Tony, > > Is it better for us to choose on `Codable` side whether `rawValue` of > `CodingKeys` should be generated with snake_case? > It seems to be more consistent with the current method of setting > `rawValue` of `CodingKeys` on `Codable` side. > > Thanks, > -- > @norio_nomura > > 2017-11-07 5:54 GMT+09:00 Tony Parker via swift-evolution < > swift-evolution@swift.org>: > >> 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