CodingKeyのマッピングを動的に切り替える - Qiita を書いてたら、なんかできそうな気がしたので類似アプローチでもうひとネタ。
やってみたらできた。
extension String {
var snakecased: String {
return self.replacingOccurrences(
of: "([A-Z])",
with: "_$1",
options: .regularExpression
).lowercased()
}
var camelcased: String {
return self
.components(separatedBy: "_")
.enumerated()
.map { 0 == $0.offset ? $0.element : $0.element.capitalized }
.joined()
}
}
struct S: Codable {
let hogeFuga: Int
enum CodingKeys: String, CodingKey {
case hogeFuga
init?(stringValue: String) {
self.init(rawValue: stringValue.camelcased)
}
var stringValue: String {
return rawValue.snakecased
}
}
}
import Foundation
let data = """
{
"hoge_fuga": 10,
}
""".data(using: .utf8)!
let decoder = JSONDecoder()
let s0 = try! decoder.decode(S.self, from: data)
print(s0)
結果:
S(hogeFuga: 10)
思ったこと
String enum
だったらrawValueだけでなんとかしなきゃいけないと思い込んでたけど、そもそもインタフェース的には init?(stringValue:)
を噛ませてるから、そこの処理を自前で書いてしまえば結構自由にフックできるんですねー。
ちなみに、CodingKeyがコンパイラに自動生成されるときのコードはこんな感じらしいです。
// enum SomeEnum {
// case A, B, C
// @derived init?(stringValue: String) {
// switch stringValue {
// case "A":
// self = .A
// case "B":
// self = .B
// case "C":
// self = .C
// default:
// return nil
// }
// }
// }
// enum SomeEnum {
// case A, B, C
// @derived var stringValue: String {
// switch self {
// case A:
// return "A"
// case B:
// return "B"
// case C:
// return "C"
// }
// }
// }