【Swift】JSONデータをデコードする方法!JSONDecoderクラスの使い方
この記事からわかること
- SwiftでJSONファイルを操作するには?
- デコードする方法
- JSONデータを構造体に紐付ける
- JSONDecoderの使い方
- スネークケース記法の対処法
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
Swiftを使ってJSON形式のファイルの中身を構造体と紐づけて変換する(デコードする)方法をまとめていきたいと思います。
JSONファイルとは?
「JSONファイル」とはJavaScriptのオブジェクト形式の記法に準拠した記述がされた拡張子が「.json」のデータファイルのことです。ちなみにJSONは「JavaScript Object Notation(記法)」の略称です。
JSON形式のデータ
{
"person": [
{
"name": "Ame Tsubaki",
"age": 18,
"hobby": ["旅行", "料理", { "web制作": ["HTML", "CSS", "php"] }]
},
{
"name": "Yoshida Takashi",
"age": 21,
"hobby": ["読書", "ランニング", "テニス"]
}
]
}
中身は配列と連想配列を組み合わせたような形式になっています。深くネストすることも可能ですが、大枠は連想配列になっているのがJSONファイルの特徴です。
Swiftでも利便性の高いJSONファイルを扱うことが可能です。
SwiftでJSONファイルを扱う
SwiftでJSONファイルを扱う上で注意しなければいけないポイントは以下の通りです。
JSONファイルを扱うポイント
- データ型
- エンコード
- デコード
エンコード(encode)とは「符号化」のことで、「一定の規則に準じた形式にデータ変換すること」です。デコード(decode)はその逆で符号化されたデータを元に戻すことをさしています。
今回はデコードについてまとめていきます。
Data型への変換
ポイント
SwiftでJSONデータを扱うには文字コード:UTF-8のData型にする
Swift内でJSONデータを扱うには適切なデータ変換が重要になってきます。JSONデータ(JSON形式の文字列)をそのまま扱おうとしてもただの文字列(String型
)として扱われてしまいます。SwiftでJSONデータとして取り扱うには文字コードがUTF-8のData型でないといけません。
例としてJSON構造の文字列を定義し、そのまま型を出力すると当然ですがString型
になります。これをData型に変換するにはString型
と同じ文字列を意味するNSString型
と呼ばれる型のdataメソッド
を使用します。
var jsonStr = """
{
"name": "Ame Tsubaki",
"age": 18,
"hobby": "web制作"
}
"""
type(of: jsonStr) //String.Type
let jsonData = String(jsonStr).data(using: .utf8)!
type(of: jsonData) // Foundation.Data.Type
dataメソッド
はData型へキャスト(型変換)するメソッドで引数(using:
)に文字コードを指定できます。つまり「.data(using: .utf8)
」は文字列をJSONが扱える状態(文字コードUTF-8/Data型)へ変換しています。返り値はオプショナル型になるので強制アンラップ(!
)しておきます。
ちなみにNSString型
とString型
の違いは使用できるプロパティやメソッドの違いです。Objective-CではNSString型
が使用されており、SwiftではString型
が追加されました。文字列を扱うという点では同じです。
まとめ
- JSONデータはUTF-8のData型として扱う
- 型の変換はNSStringのdataメソッドが便利
- 返り値はオプショナル型
JSONデータをSwift構造体と紐付ける
JSONデータをSwift内で扱いやすくするために構造体として変換(デコード)していきます。用意する構造体のプロパティはJSONデータの構造とキー値に沿ったプロパティ名にしておきます。
デコードする構造体はDecodableプロトコル
に準拠していなければいけません。またDecodableプロトコル
とEncodableプロトコル
の両方を兼ねたタイプエイリアス(別名)であるCodable
プロトコルを指定しても問題ありません。
struct Person: Decodable {
var name:String
var age:Int
var hobby:String
}
これで準備はできたので実際にデコードしてみます。今回はJSONデータは同じファイル内に定義して参照します。
var jsonStr = """
{
"name": "Ame Tsubaki",
"age": 18,
"hobby": "web制作"
}
"""
// JSONデータをString型→Data型に変換
let jsonData = String(jsonStr).data(using: .utf8)!
// JSONデータを構造体に準拠した形式に変換
let person = try! JSONDecoder().decode(Person.self, from: jsonData)
print(person.name) // Ame Tsubaki
print(person.hobby) // web制作
JSONDecoderクラスとは
JSONデータを構造体へと変換しているのは下記の部分です。
JSONDecoder().decode(Person.self, from: jsonData)
JSONDecoder()
はSwiftに標準で定義されている、JSONデータからデコードできるクラスです。その中のdecodeメソッド
の引数に変換したい型(構造体)と対象のデータを渡すことでデコードされます。
decodeメソッドの定義と使い方
decodeメソッド
の定義を見てみると以下のようにthrows
句が入っています。throws
句があるメソッドは例外が投げられる可能性があるのでtry文
を使って例外を処理できるようにしておく必要があります。
func decode<T>(T.Type, from: Data) -> T throws -> T where T : Decodable
try文
にも種類がありますが、try!
は例外が発生しても強制的に無視します。万が一例外が発生すれば停止します。<T>の意味がわからない方は以下の記事を参考にしてください。
成功すれば変数に構造体に準拠したデータが格納されます。あとはドットシンタックスを使ってそれぞれのプロパティへアクセス可能です。
print(person.name) // Ame Tsubaki
print(person.hobby) // web制作
まとめ
- JSONを変換する構造体はDecodableプロトコルに準拠させる
- String型→Data型→構造体に変換
- JSONDecoderクラスのdecodeメソッドを用いてデコードする
ネストされた複数行のJSONデータをデコードする
JSONデータがネスト(階層化)されていたり、複数行ある場合(配列)を見ていきます。
ネストされたJSONデータ
var jsonStr = """
{
"schoolName": "Webエンジニア学習部屋",
"person": [
{
"name": "Ame Tsubaki",
"age": 18,
"hobby": "web制作"
},
{
"name": "Yoshida Takashi",
"age": 21,
"hobby": "読書"
}
]
}
"""
まずは受け取る構造体をネストしている分定義します。school
構造体の中にはさらに配列[]にしたPerson構造体を定義しておきます。
struct School: Decodable {
var schoolName:String
var person:[Person]
}
struct Person: Decodable {
var name:String
var age:Int
var hobby:String
}
JSONデータの変換は先ほどと同様に行えますが、参照方法には注意が必要です。school.person
ではPerson
構造体を値に持った配列が渡されます。なのでfor
文で回して取り出すことも可能になります。
let jsonData = String(jsonStr).data(using: .utf8)!
let school = try! JSONDecoder().decode(School.self, from: jsonData)
print(school.schoolName) // Webエンジニア学習部屋
print(school.person) // [Person(name: "Ame Tsubaki", age: 18, hobby: "web制作"), Person(name: "Yoshida Takashi", age: 21, hobby: "読書")]
for people in school.person {
print(people.name) // Ame Tsubaki , Yoshida Takashi
}
JSONデータに存在しないプロパティを構造体に持たせる
School
構造体にJSONデータにはないプロパティを定義しようとするとエラーになってしまいます。
エラーになってしまう構造体定義
struct School: Decodable {
var schoolName:String
var person:[Person]
var address:String = "" // JSONデータにはないプロパティを定義したい
}
Fatal error: 'try!' expression unexpectedly raised an error: debugDescription: "No value associated with key CodingKeys
// 致命的なエラー:「試してみてください!」式で予期せずエラーが発生しました:debugDescription:"キーCodingKeysに関連付けられた値がありません
これを防ぐにはデコードしてほしいプロパティを明示的に指定することで解決することができます。指定するには列挙型(enum)CodingKeys
を構造体内に記述します。名称は「CodingKeys」と決まっています。
プロパティを明示的に指定した構造体定義
struct School: Decodable {
var schoolName:String
var person:[Person]
var address:String = "" // 初期値を指定しないとエラーになります
private enum CodingKeys: String, CodingKey {
case schoolName, person
}
}
キー名とプロパティ名が異なる場合の対処法
JSONのキーがsnake_caseのようなスネークケース記法で記述されている場合、キャメルケースで記述することの多いSwiftでは厄介です。
{
“snake_case”: “Test”,
"CamelCase": 20,
}
その場合はCodingKeys
に以下のように対象のJSONキー名を記述することで自動で変換してくれるようになります。
struct MyObject: Decodable {
let SnakeCase: String
let CamelCase: Int
enum CodingKeys: String, CodingKey {
// プロパティ名 = "JSONキー名"
case SnakeCase = "snake_case"
case CamelCase
}
}
true/false以外を真偽値へ変換する方法
JSONの値が以下のようにtrue/false以外でBool型へ変換させたい場面もあると思います。
対象のJSON
{
"key1": 1,
"key2": 0,
}
詳細はこちらの記事を参考にしてください。
Date型をJSONにする際の注意点
SwiftでDate
型をプロパティに持つ構造体などをJSONに変換したい場合は特に意識せずに相互に変換が可能になります。
struct AppNotify: Identifiable, Codable {
public var id: UUID = UUID()
public var title: String
public var msg: String
public var time: Date = Date()
}
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrintedguard
let jsonData = try? encoder.encode(notification) else { return }
guard let json = String(data: jsonData, encoding: .utf8) else { return }
print(json)
例えば上記の構造体をそのまま変換すると以下のように741670569.590185
というエポック秒形式でJSON化されます。
{
"time": 741670569.590185,
"msg": "通知メッセージ",
"title": "通知タイトル",
"id": "7964108A-4F26-4DD1-B421-1607AA1EA3E6"
}
しかしここで生成されたエポック秒は情報が抜け落ちているのでSwift内でデコードする際は問題ないですがAndroidなどで変換しようとした際に正しい日付に変換できません。解消法は以下の記事を参考にしてください。
もっと簡単にJSONデータを操作する方法
デフォルトで組み込まれているJSONSerialization
クラスや外部ライブラリのSwiftyJSONなどを使用すればJSONを辞書型や指定の構造体に簡単に変換でき、操作することが可能です。
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。