プロパティが nil の場合もエンコード先の JSON に属性を出力する
はじめに
- JSON の解析をカスタマイズする方法については [ネストした JSON をフラットな構造体にマッピングする] で書きました
- 「
Codable
なオブジェクトに nil 値があると、 JSON ではその属性自体が省略されてしまう挙動を変えられないのか?」という質問を受講者から頂いたので、ここに記載しておきます
検証環境
- macOS Big Sur 11.6
- Xcode 13.2.1
サンプルコード
通常の挙動(nil を含む属性が出力されない)
// JSON と対応させる Person 型(Codable に準拠)
struct Person: Codable {
let name: String
let age: Int? // nil を許容
}
let encoder = JSONEncoder()
let yamada = Person(name: "山田二郎", age: 53)
let kawada = Person(name: "川田吾郎", age: nil)
let yamadaData = try! encoder.encode(yamada)
let kawadaData = try! encoder.encode(kawada)
print("==== 値が nil の属性は出力されない ====")
print(String(data: yamadaData, encoding: .utf8)!)
print(String(data: kawadaData, encoding: .utf8)!)
実行結果
==== 値が nil の属性は出力されない ====
{"name":"山田二郎","age":53}
{"name":"川田吾郎"}
nil を含む属性を出力するように変更したもの
// JSON と対応させる CustomPerson 型(Codable に準拠)
struct CustomPerson: Codable {
let name: String
let age: Int? // nil を許容
}
// Encodable プロトコルの encode(to:) メソッドをオーバーライド
// Codable は Encodable & Decodable 型
extension CustomPerson {
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.name, forKey: .name)
// 通常は nil なら無視されるが、明示的にこのフィールドを encode 処理する
try container.encode(self.age, forKey: .age)
}
}
let sunagawa = CustomPerson(name: "砂川黄太郎", age: 87)
let umino = CustomPerson(name: "海野泳太郎", age: nil)
let sunagawaData = try! encoder.encode(sunagawa)
let uminoData = try! encoder.encode(umino)
print("==== 値が nil の属性も出力される ====")
print(String(data: sunagawaData, encoding: .utf8)!)
print(String(data: uminoData, encoding: .utf8)!)
実行結果
==== 値が nil の属性も出力される ====
{"name":"砂川黄太郎","age":87}
{"name":"海野泳太郎","age":null}
まとめ
encode(to:)
をオーバーライドして、全項目を明示的にエンコードするだけなので、それほど難しくはないです
- ただ、若干の手間なので、単にエンコード時にプロパティ指定をするなどの方法があると良いですね
- サンプルは GitHub に置きました