【Swift】JSONデータをデコードする方法!JSONDecoderクラスの使い方

この記事からわかること

  • SwiftJSONファイル操作するには?
  • デコードする方法
  • 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データを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データをデコードする

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,
}

詳細はこちらの記事を参考にしてください。

もっと簡単にJSONデータを操作する方法

デフォルトで組み込まれているJSONSerializationクラスや外部ライブラリのSwiftyJSONなどを使用すればJSONを辞書型や指定の構造体に簡単に変換でき、操作することが可能です。

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index