Converting between camelCase and snake_case with Codable
Using JSONDecoder's keyDecodingStrategy and keyEncodingStrategy to absorb the difference in naming conventions without hand-writing CodingKeys.
- Published
This article is also published elsewhere. https://qiita.com/iganin/items/6c6a588dd73731097698
Originally written in Japanese. This is a translation of the same piece.
Introduction
With Xcode 9.3, the default Swift version goes from 4.0 to 4.1. There are a number of improvements, and one
of them looks useful around Codable, so I am sharing it as a note to self.
What follows is based on the beta as of 20 February. It may change by the time the final version ships, so please keep that in mind.
camelCase and snake_case
On iOS it is common to use camelCase for property names. A struct definition might look like this:
struct Person: Codable {
let firstName: String
let lastName: String
let age: String
let gender: String
}
Meanwhile, server API responses sometimes use snake_case:
{
"first_name": "Tetsuya",
"last_name": "Hayashi",
"age": "30",
"gender": "male"
}
Trying to parse a response like that with the Codable-conforming struct above does not work as-is, because
firstName and first_name are different names. Until now you had to either write Person in snake_case
or define CodingKeys separately, like this. It is bearable with only a few properties, but defining
CodingKeys every time feels redundant.
struct Person: Codable {
let firstName: String
let lastName: String
let age: String
let gender: String
private enum CodingKeys: String, CodingKey {
case firstName = "first_name"
case lastName = "last_name"
case age
case gender
}
}
Swift 4.1 adds properties to JSONDecoder and JSONEncoder that solve this.
keyDecodingStrategy and keyEncodingStrategy
JSONDecoder gains a keyDecodingStrategy property and JSONEncoder a keyEncodingStrategy. Setting them
makes converting between camelCase and snake_case straightforward.
Parsing the earlier JSON into the Person struct now looks like this. No more CodingKeys — much tidier.
let personJson = """
{
"first_name": "Tetsuya",
"last_name": "Hayashi",
"age": "30",
"gender": "male"
}
""".data(using: .utf8)!
let jsonDecoder = JSONDecoder()
jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase // convert from snakeCase here
do {
let person = try jsonDecoder.decode(Person.self, from: personJson)
print(person)
} catch {
print(error.localizedDescription)
}
The reverse works too — converting camelCase to snake_case when encoding:
let person = Person(firstName: "Tetsuya", lastName: "Hayashi", age: "30", gender: "male")
let jsonEncoder = JSONEncoder()
jsonEncoder.keyEncodingStrategy = .convertToSnakeCase // convert to snakeCase here
do {
let json = try jsonEncoder.encode(person)
print(json)
} catch {
print(error.localizedDescription)
}
Thoughts
Codable already struck me as a big convenience when it arrived, and it keeps getting better. Writing code
is going to be that much more fun.