@@ -44,6 +44,9 @@ public struct KeyValueDecoder: Sendable {
4444 /// The strategy to use for decoding BinaryInteger types. Defaults to `.exact` for lossless conversion between types.
4545 public var intDecodingStrategy : IntDecodingStrategy = . exact
4646
47+ /// The strategy to use for decoding each types keys.
48+ public var keyDecodingStrategy : KeyDecodingStrategy = . useDefaultKeys
49+
4750 /// Initializes `self` with default strategy.
4851 public init ( ) {
4952 self . userInfo = [ : ]
@@ -82,6 +85,15 @@ public struct KeyValueDecoder: Sendable {
8285 /// Floating point conversions are also clamped, rounded when a rule is provided
8386 case clamping( roundingRule: FloatingPointRoundingRule ? )
8487 }
88+
89+ /// Strategy to determine how to decode a type’s coding keys from String values.
90+ public enum KeyDecodingStrategy : Sendable {
91+ /// A key decoding strategy that converts snake-case keys to camel-case keys.
92+ case convertFromSnakeCase
93+
94+ /// A key encoding strategy that doesn’t change key names during encoding.
95+ case useDefaultKeys
96+ }
8597}
8698
8799#if canImport(Combine)
@@ -105,12 +117,14 @@ private extension KeyValueDecoder {
105117 struct DecodingStrategy {
106118 var optionals : NilDecodingStrategy
107119 var integers : IntDecodingStrategy
120+ var keys : KeyDecodingStrategy
108121 }
109122
110123 var strategy : DecodingStrategy {
111124 DecodingStrategy (
112125 optionals: nilDecodingStrategy,
113- integers: intDecodingStrategy
126+ integers: intDecodingStrategy,
127+ keys: keyDecodingStrategy
114128 )
115129 }
116130
@@ -381,7 +395,8 @@ private extension KeyValueDecoder {
381395
382396 func container( for key: Key ) throws -> SingleContainer {
383397 let path = codingPath. appending ( key: key)
384- guard let value = storage [ key. stringValue] else {
398+ let kkk = strategy. keys. makeStorageKey ( for: key. stringValue)
399+ guard let value = storage [ kkk] else {
385400 let keyPath = codingPath. makeKeyPath ( appending: key)
386401 let context = DecodingError . Context ( codingPath: codingPath, debugDescription: " Dictionary does not contain key \( keyPath) " )
387402 throw DecodingError . keyNotFound ( key, context)
@@ -395,7 +410,7 @@ private extension KeyValueDecoder {
395410 }
396411
397412 func contains( _ key: Key ) -> Bool {
398- return storage [ key. stringValue] != nil
413+ return storage [ strategy . keys . makeStorageKey ( for : key. stringValue) ] != nil
399414 }
400415
401416 func decodeNil( forKey key: Key ) throws -> Bool {
@@ -640,6 +655,16 @@ private extension KeyValueDecoder {
640655 }
641656}
642657
658+ extension KeyValueDecoder . KeyDecodingStrategy {
659+
660+ func makeStorageKey( for key: String ) -> String {
661+ switch self {
662+ case . useDefaultKeys: return key
663+ case . convertFromSnakeCase: return key. toSnakeCase ( )
664+ }
665+ }
666+ }
667+
643668extension BinaryInteger {
644669
645670 init ? ( from source: Double , using strategy: KeyValueDecoder . IntDecodingStrategy ) {
0 commit comments