2018/03/27更新

[Swift] JSON文字列から任意のオブジェクトへ変換する(JSONDecoderとCodableの利用)

このエントリーをはてなブックマークに追加            

こんにちは、@yoheiMuneです。
Swift4からCodableが追加されて、JSON文字列から任意のオブジェクトに簡単に変換できるようになりました。今日はその方法をブログに書きたいと思います。



目次




対象のバージョン

このブログでの記載は、Swift4以上を対象としています。



何がしたいのか

Swift3以前だと、JSON文字列からデータを取り出すためにSwiftyJSONなどを使う必要があり、なかなか煩雑でした。しかしSwift4から追加されたCodableJSONDecoderを用いることで、簡単にJSON文字列をデコードすることができます。
今日は、Swift4におけるJSONの扱いをブログに書きたい次第です。



JSON文字列 -> 任意のオブジェクトへの変換

それではここから具体的な使い方を見ていきたいと思います。

基本的な使い方

例えば以下のようなJSON文字列があったとします。
let jsonStr = """
{
    "name" : "Munesada",
    "age" : 32
}
"""
これを読み込んで、以下のデータ型にマッピングしたいとします。
struct User: Codable {  // Codableインターフェースを実装する
    let name: String
    let age: Int
}
ここでポイントはCodableを継承することです。この状態で、以下のようにJSONDecoderを使うことで、JSON文字列をUserオブジェクトに変換することができます。
// String -> Data に変換
let jsonData = jsonStr.data(using: .utf8)

// JSONをデコード
let user = try! JSONDecoder().decode(User.self, from: jsonData!)

// 結果を表示してみる.
print("name: \(user.name)")  // Munesada
print("age: \(user.age)")    // 32
このように、簡単にJSON文字列をオブジェクトに変換することができます。

なお、オブジェクトの型を用意するのが面倒と思うかもしれませんが、実際の開発ではAPIのレスポンス結果は特定の形を想定しているので、このようなクラスを作成する方が実装効率は良いです。

オプショナル型を扱う

API結果(JSON)によっては、一部のプロパティが省略される場合があります。その場合には、省略されるプロパティをオプショナル型として定義することで取り込むことができます。
struct User: Codable {
    let name: String
    let age: Int
    let message: String?  // オプショナル型にする
}
この場合、messageは省略されたらnilになります。
// messageが省略された場合
let jsonStr = """
{
    "name" : "Munesada",
    "age" : 32
}
"""
let jsonData = jsonStr.data(using: .utf8)
let user = try! JSONDecoder().decode(User.self, from: jsonData!)

// デコード結果はnilになる.
print("message: \(user.message ?? "ないよー")")  // ないよー

ネストにも対応できる

API結果(JSON)が1階層であることは少なく、2階層以上あることが多いと思います。JSONデコードはネストし構造にも対応できます。
例えば、以下のようにGroup -> Userとネストしているとします。
struct Group: Codable {
    let groupName: String

    // Userを保持している(=ネスト)
    let users: [User]
}
この場合にも、今まで同じような実装でデコードが可能です。
// ネストされたJSON
let jsonStr = """
{
    "groupName": "グループ名",
    "users" : [{
        "name" : "Munesada",
        "age" : 32
    }, {
        "name" : "Yamada",
        "age" : 28
    }]
}
"""
let jsonData = jsonStr.data(using: .utf8)
let group = try! JSONDecoder().decode(Group.self, from: jsonData!)

// 結果の確認.
print("groupName: \(group.groupName)") // グループ名
print("users: \(group.users)") // users: [User(name: "Munesada", age: 32, message: nil), User(name: "Yamada", age: 28, message: nil)]

ルートが配列の場合

API結果について、ルートが以下のように配列である場合も対応が可能です。
// ルートがArrayの場合
let jsonStr = """
[
  { "name" : "Yohei", "age" : 32 },
  { "name" : "Kengo", "age" : 42 }
]
"""
この場合は、decode関数に渡す形を[User].selfとします。
let jsonData = jsonStr.data(using: .utf8)
let users = try! JSONDecoder().decode([User].self, from: jsonData!)
print("users: \(users)")

Date型へのパース

Date型へのパースも機能として提供されています。例えば以下の構造体にマッピングしたいとします。
// Date型のフィールドを保持する構造体
struct MyDate: Codable {
    let dt: Date
}
JSONは以下の内容とします。
let jsonStr = """
{ "dt" : "2018-03-03T12:13:31Z" }
"""
このデータをデコードする場合に、JSONDecoderのインスタンスでdateDecodingStrategyを指定することで、日付型へマッピングできるようになります。
let jsonData = jsonStr.data(using: .utf8)

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601  // 日付フォーマットを指定

// 後は普通にデコードする.
let myDate = try! decoder.decode(MyDate.self, from: jsonData!)
print("myDate: \(myDate)")
dateDecodingStrategyで指定可能な値は、公式ドキュメントを参照ください。



参考資料

CodableとJSONDecoderについて、以下の資料が参考になります。ありがとうございます。

Codable - Swift Standard Library | Apple Developer Documentation(公式サイト)

JSONDecoder - Foundation | Apple Developer Documentation(公式サイト)

Encoding and Decoding Custom Types | Apple Developer Documentation(公式サイト)

[Swift 4] SwiftyJSONを使わずにシンプルにJSONをデータ構造化する | Developers.IO

Ultimate Guide to JSON Parsing with Swift 4

Codableで色々なJSONに対応する - Qiita



最後に

今回は構造体(struct)へマッピングしましたが、クラス(class)にも同じようにマッピング可能です。形のあるデータ構造へ簡単にマッピングできるのは嬉しいですね。

最後になりますが本ブログでは、フロントエンド、Swift、PHP、Node.js、Python、Linux、Java、インフラ、Go言語、機械学習、などの技術トピックを発信をしていきます。「プログラミングで困ったその時の、解決の糸口に!」そんな目標でブログを書き続けています。ぜひ、本ブログのRSSTwitterをフォローして貰えたら嬉しいです ^ ^

最後までご覧頂きましてありがとうございました!





こんな記事もいかがですか?

RSS画像

もしご興味をお持ち頂けましたら、ぜひRSSへの登録をお願い致します。