4
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

Swiftの標準関数でJSONをデコードする。SwiftyJSONからの脱却。

なるべく純粋なSwiftでアプリを作りたい。

ライブラリはなるべく入れたくない!
そう思った事ないですか?

Swiftには、JSONDecoderと言うJSONをデコードするための標準の機能があります。
このJSONDecoderがとても優れているので、今回記事を書きました。

手始めにJSONのデコードに使うSwiftyJsonを、純粋なSwiftで置き換えた所メリットしかなかったので、ここにメモします。
・パフォーマンスがJsonDecoderの方がSwiftyJsonより優れている。
・依存関係を一つ減らせる。
など。

JSONDecoderの基本的な使い方

1. Decodableを、structへ継承する。

継承するだけで、struct/classをデコード可能にしてくれる優れものです。

import Foundation

struct User: Decodable {
  var name: String
  var age: Int
}

2. jsonをDataへと変換する。


let json = """
{
  "name": "Bob",
  "age": 20,
}
"""
let jsonData = json.data(using: .utf8)!

3. JSONDecoderでデコードする。


let user = try JSONDecoder().decode(User.self, from: jsonData)

以上です。
structに、jsonがstructに格納されました。
シンプルで使いやすいですね。


print(user.name) // Bob
print(user.age) // 20

配列内のjsonからデータを取り出す

もちろんルートが配列のJSONもデコード出来る。
また、jsonにかけているキーがある場合、オプショナル型で表現する。


import Foundation

let json = """
[
    {
        "name": "Bob",
        "age": 30,
        "password": "foo"
    },
    {
        "name": "Ben",
        "age": 20
    }
]
""".data(using: .utf8)!

struct User: Decodable {
    var name: String
    var age: Int
    var password: String?
}

let users = try JSONDecoder().decode([User].self, from: json)

for user in users {
    print(user)
}
// 結果
// User(name: "Bob", age: 30, password: Optional("foo"))
// User(name: "Ben", age: 20, password: nil)

キーの名前を変更する

jsonのキーと、swiftのオブジェクトのキーの名前が異なる事がある。
例えばjsonのキーがuser_id、swiftオブジェクトのキーがuserIdだった時などだ。
このままではデコード出来ないので、Decodableの、CodingKeys enumを使用し、デコード可能にする。


import Foundation

let json = """
[
    {
        "name": "Bob",
        "age": 30,
        "password": "foo",
        "user_id": 1
    },
    {
        "name": "Ben",
        "age": 20,
        "user_id": 2
    }
]
""".data(using: .utf8)!

struct User: Decodable {
    var name: String
    var age: Int
    var password: String?
    var userId: Int

    private enum CodingKeys: String, CodingKey {
        case name
        case age
        case password
        case userId = "user_id"
    }
}

let users = try JSONDecoder().decode([User].self, from: json)

for user in users {
    print(user)
}

無事に、user_idを、userIdへとマッピングする事が出来ました。

ネストしたjsonからデータを取り出す

Decodableは、ネストさせる事が出来る。


struct User: Decodable {
    let name: String
    let children: [Child]

    struct Child: Decodable {
        let name: String
        let teachers: [Teacher]

        struct Teacher: Decodable {
            let name: String
        }
    }
}

そのため、簡単にネストしたJSONのデータ構造を表す事が可能。

import Foundation
let json = """
[
    {
        "name": "A",
        "children": [
            {
                "name": "B",
                "teachers": [
                    {
                        "name": "C"
                    }
                ]
            }
        ]
    },
    {
        "name": "E",
        "children": [
            {
                "name": "F",
                "teachers": [
                    {
                        "name": "G"
                    },
                    {
                        "name": "I"
                    }
                ]
            }
        ]
    }
]
""".data(using: .utf8)!

let users = try JSONDecoder().decode([User].self, from: json)
// [
//   User(
//     name: "A",
//     children: [
//       Child(
//         name: "B",
//         teachers: [ Teacher(name: "C") ]
//       )
//     ]
//   ),
//   User(
//     name: "E",
//     children: [
//       Child(
//         name: "F",
//         teachers: [ Teacher(name: "G"), Teacher(name: "I") ]
//       )
//     ]
//   )
// ]

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
4
Help us understand the problem. What are the problem?