【Swift】JSONの1や0をBool(真偽値)に変換する方法!decodeIfPresent
この記事からわかること
- JSONDecoderの使い方
- JSONの値が1や0の時にBool(真偽値)へ変換する方法
- APIで真偽値を扱う
- decodeIfPresentメソッドの使い方
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
JSONデータの1や0をBool(真偽値)に変換する方法
iOSアプリからAPIと連携しJSONデータを取得しSwiftで扱えるオブジェクトに変換する際に、JSONの値が以下のように1
や0
の場合がありました。ただInt
型として扱うなら良いですがBool
型を意味する値だったので1
や0
を渡された場合にBool
型に変換する方法をまとめていきます。
JSONは以下のように値部分に数値の1
や0
が渡されます。
対象のJSON
{
"key1": 1,
"key2": 0,
}
マッピング用のクラスにBool型への変換のロジックを含ませます。変換したいキーにあったプロパティを準備しCodingKey
も定義します。CodingKey
がないと後続の処理でエラーが発生します。
JSONからオブジェクトへの変換作業は自動で行われますが、専用のイニシャライザを用意することで変換時の値を操作することが可能になります。中ではdecodeIfPresent(_:forKey:)
メソッドを使用してキー1つ1つをデコードしていきます。
マッピング用クラス
struct MyObject: Decodable {
let key1: Bool
let key2: Bool
enum CodingKeys: String, CodingKey {
case key1, key2
}
// Bool型へ変換用のイニシャライザ
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let intValue = try container.decodeIfPresent(Int.self, forKey: .key1) {
key1 = (intValue == 1) // 1ならtrue
} else {
key1 = false
}
if let intValue = try container.decodeIfPresent(Int.self, forKey: .key2) {
key2 = (intValue == 1) // 1ならtrue
} else {
key2 = false
}
}
}
公式リファレンス:decodeIfPresent(_:forKey:)
今回は1
や0
の際にBool型へ変換していますが、値によって任意の値に変換させることも可能になります。
使用例
// JSONデータ
let jsonData = """
{
"key1": 1,
"key2": 0,
}
""".data(using: .utf8)!
// JSONデコード
do {
let decoder = JSONDecoder()
let instance = try decoder.decode(MyObject.self, from: jsonData)
print(instance.key1) // true
print(instance.key2) // false
} catch {
print("JSON decoding error: \(error)")
}
decodeIfPresentのtry
func decodeIfPresent(_ type: Bool.Type, forKey key: Self.Key) throws -> Bool?
decodeIfPresent
メソッドは例外を投げる可能性があるメソッドです。そのためJSONの値と正しいデータ型を指定していれば成功しますが、異なるデータ型を指定すると失敗してしまいます。try
だと失敗した際にデコード自体が失敗になってしまうのでtry?
を使うことで失敗してもnilが格納されてdecodeメソッドも継続させることが可能です。
let intValue = try container.decodeIfPresent(Int.self, forKey: .key1) // 成功
let stringValue = try container.decodeIfPresent(String.self, forKey: .key1) // 失敗
// ここで例外が発生しdecodeメソッドが終了してしまう。
let intValue = try container.decodeIfPresent(Int.self, forKey: .key1) // 成功
let stringValue = try? container.decodeIfPresent(String.self, forKey: .key1) // 失敗
// try?を使うことで失敗してもnilが格納されてdecodeメソッドも継続する
JSONに複数の値がある場合
取得できるJSONがBool型への変換だけでなく以下のようにString
型やInt
型に変換したい場合もあると思います。
{
"key1": 1,
"key2": 0,
"key3": "test",
"key4": 20,
}
この場合も実装方法はイニシャライザの中にキー1つ1つデコードする処理を記述する必要があります。構造体のプロパティに初期値があればデコードしなくてもその初期値が使用されてしまいますがデコードが不要にもできます。
struct MyObject: Decodable {
let key1: Bool
let key2: Bool
let key3: String
let key4: Int
enum CodingKeys: String, CodingKey {
case key1, key2, key3, key4
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// Boolへ任意のデコード
if let intValue = try container.decodeIfPresent(Int.self, forKey: .key1) {
key1 = (intValue == 1)
} else {
key1 = false
}
if let intValue = try container.decodeIfPresent(Int.self, forKey: .key2) {
key2 = (intValue == 1)
} else {
key2 = false
}
// 通常のデコード
key3 = try container.decodeIfPresent(String.self, forKey: .key3) ?? ""
key4 = try container.decodeIfPresent(Int.self, forKey: .key4) ?? 0
}
}
エンコードする際にBool型をInt型に変換する方法
反対にSwiftのオブジェクトのプロパティがBool
型の際にJSONで1
や0
などのように任意の形に変換することも可能です。まずはオブジェクトにencode(to:)
メソッドを追加します。
struct MyObject: Decodable, Encodable {
let key1: Bool
let key2: Bool
enum CodingKeys: String, CodingKey {
case key1, key2
}
// Bool型へ変換用のイニシャライザ
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let intValue = try container.decodeIfPresent(Int.self, forKey: .key1) {
key1 = (intValue == 1) // 1ならtrue
} else {
key1 = false
}
if let intValue = try container.decodeIfPresent(Int.self, forKey: .key2) {
key2 = (intValue == 1) // 1ならtrue
} else {
key2 = false
}
}
// エンコード
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
// Bool型から0/1へ変換してエンコード
let intValue = key1 ? 1 : 0
try container.encode(intValue, forKey: .key1)
// Convert Bool to Int for encoding
let intValue2 = key2 ? 1 : 0
try container.encode(intValue2, forKey: .key2)
// カスタムで変換しない場合のエンコード
// try container.encode(key3, forKey: .key3)
}
}
ここでencode(_:forKey:)
メソッドを使用してキーに応じた変換値を指定します。今回はBoolから1/0なので上記のような実装になっています。
func encode(
_ object: Any?,
forKey key: String
)
あとは普通にエンコード処理を実装するだけで変換することが可能です。
// JSONデコード
do {
let decoder = JSONDecoder()
let instance = try decoder.decode(MyObject.self, from: jsonData)
let encoder = JSONEncoder()
// フォーマットを指定
encoder.outputFormatting = .prettyPrinted
// エンコード
let jsonData = try encoder.encode(instance)
print(String(data: jsonData , encoding: .utf8)!)
} catch {
print("JSON decoding error: \(error)")
}
変換されたJSON
{
"key1" : 1,
"key2" : 0
}
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。