Support either kebab-case or PascalCase decoding by extending JSONDecoder
It is common to find RESTful APIs returning JSON payloads using either the kebab-case
or PascalCase
format. The first approach for tackling this situation may be writing the implementation of the CodingKey
protocol conformance. The main disadvantage of going this way is that we would have to write the implementations for the CodingKey
conformances of every Decodable
model.
On the other hand, JSONDecoder
allows setting the strategy to use to decode JSON data. It has two built-in strategies and a third option for custom writing:
useDefaultKeys
- default strategy setconvertFromSnakeCase
custom(@Sendable (_ codingPath: [CodingKey]) -> CodingKey)
So, a different approach to achieving the custom decoding for either case would be writing an extension to do so.
convertFromKebabCase
:
public extension JSONDecoder.KeyDecodingStrategy {
/// A key decoding strategy that converts `kebab-case` keys to `camelCase` keys.
static var convertFromKebabCase: JSONDecoder.KeyDecodingStrategy {
.custom { codingPath in
let codingKey = codingPath[codingPath.endIndex.advanced(by: -1)]
guard codingKey.intValue == nil else {
return codingKey
}
let components = codingKey
.stringValue
.components(separatedBy: "-")
let head = components
.prefix(1)
.map(\.localizedLowercase)
let tail = components
.dropFirst()
.map(\.capitalized)
return type(of: codingKey)
.init(
stringValue: (head + tail)
.joined()
) ?? codingKey
}
}
}
convertFromPascalCase
:
public extension JSONDecoder.KeyDecodingStrategy {
/// A key decoding strategy that converts `PascalCase` keys to `camelCase` keys.
static var convertFromPascalCase: JSONDecoder.KeyDecodingStrategy {
.custom { codingPath in
let codingKey = codingPath[codingPath.endIndex.advanced(by: -1)]
guard codingKey.intValue == nil else {
return codingKey
}
let head = codingKey
.stringValue
.prefix(1)
.lowercased()
let tail = codingKey
.stringValue
.dropFirst()
return type(of: codingKey)
.init(stringValue: head + tail) ?? codingKey
}
}
}
Then, all we have to do is to create an instance of JSONDecoder
, set the key decoding strategy, and use it, and that's it.