JSONからCodable化されたstructを自動生成するツールを作った話

## はじめに

皆さん，Codableは使用していますか? 便利ですよね。

CodableによってJSONが簡単に扱えるようになった一方で，まだまだ途上という段階で，扱いづらさを覚えている方もいらっしゃるのではないでしょうか。

中でも，

[Codable: Tips and Tricks](http://kean.github.io/post/codable-tips-and-tricks)
> One of the major downsides of Codable is that as soon as you need custom decoding logic - even for a single key - you have to provide custom everything: manually define all the coding keys, and implementing an entire init(from decoder: Decoder) throws initializer by hand. This isn’t ideal.

とあるように，現在 (Swift 4.2) のCodableでは，(`init`は省略できるようになったものの) 対応するCodingKeysや，JSONの返すKeyに不足がある場合`JSONDecoder().decode()`が失敗するなど，面倒な場面があります。

自分で管理するAPIならまだしも，公開されているAPIで「何がOptionalで欠損しているのかが分からない!」といった場面に遭遇した際にはかなり苦戦させられます。
手動で「?」を付けたり，CodingKeyを追加したり。イヤですよね。

そこで，その作業を自動化するツールを作成しました。

## 作成したもの

[JSONtoCodable](https://github.com/YutoMizutani/JSONtoCodable) という，生のJSONからCodable化されたstructを吐き出すツールを作成しました。

![demo_macos.png](https://raw.githubusercontent.com/YutoMizutani/JSONtoCodable/media/media/demo_macos.png)

Demoをダウンロードし，JSONテキストを貼り付けて「Generate!」ボタンを押すと，よしなに解析，整形されたstructテキストを出力します。

例えば，

```js
{
    "user": {
        "Name": "Yuto Mizutani"
    },
    "lib": {
        "lib-name": "JSONtoCodable",
        "year": 2018,
        "version": "1.0.2",
        "released": "2018-09-22"
    },
    "text": "Hello, world!!"
}
```

というJSONを入力した場合，

```swift
public struct Result: Codable {
    public let user: User
    public let lib: Lib
    public let text: String

    public struct User: Codable {
        public let name: String

        private enum CodingKeys: String, CodingKey {
            case name = "Name"
        }
    }

    public struct Lib: Codable {
        public let libName: String
        public let year: Int
        public let version: String
        public let released: String

        private enum CodingKeys: String, CodingKey {
            case libName = "lib-name"
            case year
            case version
            case released
        }
    }
}
```

というstructが生成されます。

CodingKeysは不要な場合省略され，JSONのKeyに対応した順序でstructに変換されていきます。
また，`"foo": ["bar", "baz"]`といった配列にも対応し，もし配列がオブジェクトであった場合には，欠損値に応じてOptional型に変換されます。

出力された値を貼り付けるだけでResponseまたはModelが完成するため，(特に初心者の方の) 開発を助けること間違いなしです!

## Q. 何で作ったの?

[「JSON Codable Swift」](https://github.com/search?q=JSON+Codable+Swift) などと[GitHub](https://github.com)で検索すれば確かに存在するように思えます。

一方で，[5以上のネストされたオブジェクトに対応していなかった](https://github.com/zadr/j2s)り， [オブジェクトが辞書型になってしまっていた](https://github.com/yonaskolb/Codability)り，[CodingKeyが機能せず変数名に使えない名前が定義される](https://github.com/hhfa008/SwiftAI) など，イマイチな代物ばかりでした。

誰もこの [ロックなJSON](https://api.engineeringvillage.com/EvDataWebServices/ev/results.json) をパスできなかったのです!


[JSONtoCodable](https://github.com/YutoMizutani/JSONtoCodable)はオブジェクトがいくつネストしていても変換できます。

## Q. 対応している型は?

現在は [JSONtoCodable#translations](https://github.com/YutoMizutani/JSONtoCodable#translations) の基本型のみです。将来的にDateやURLにも対応できるようにする予定です。

## Q. 使用する上での注意は?

現在，JSONのvalueがStringとNumber型，または配列と値による複数型であった場合，エラーとして失敗せず，Any型や重複されたimmutableとして出力できてしまうようになっています。Swift4.2ではまだCodable内のAny型はコンパイルエラーとなってしまいます。
これは，Swift側で実行時にJSON側のエラーと見分けられるようにするため，現在は意図的にそうしています。エラーとして返してしまうと，どういう結果であったのか，すぐ直せるようなJSONのミスをError型で書き消してしまうのを防ぐためです。

## Q. このJSON上手くいかないんだけど? / なんかコード汚ないんだけど?

上手くいかないケースがありましたら下記 issues にお願い致します。日本語でも対応致します。
可能であればJSONの失敗例がありますと，TDDで解決しやすくなり大変助かります!
https://github.com/YutoMizutani/JSONtoCodable/issues

コードが汚ないと感じた方，下記よりお待ちしております!
https://github.com/YutoMizutani/JSONtoCodable/pulls

## おわりに

まずは，[Demo](https://github.com/YutoMizutani/JSONtoCodable/tree/master/Demo)を試していただきたいです。

そして，よければスターをお願い致します!

皆さんの開発ライフを少しでも手助けできれば幸いです。

## 追記 (2018/10/25)

[CLIに対応](https://github.com/YutoMizutani/JSONtoCodable#jc-cli-app)しました!
curl等からパイプで繋いで生成することができるようになりました!

#### インストール

```
$ brew tap YutoMizutani/jc
$ brew install jc
```

#### 使い方

```
$ curl https://httpbin.org/get | jc
$ curl https://httpbin.org/get | jc > Result.swift
```

![screenshot](https://raw.githubusercontent.com/YutoMizutani/JSONtoCodable/media/media/demo_cli.png)

## References

- [JSONtoCodable - https://github.com/YutoMizutani/JSONtoCodable](https://github.com/YutoMizutani/JSONtoCodable)
- [Codable: Tips and Tricks - http://kean.github.io/post/codable-tips-and-tricks](http://kean.github.io/post/codable-tips-and-tricks)
- [zadr/j2s - https://github.com/zadr/j2s](https://github.com/zadr/j2s)
- [yonaskolb/Codability - https://github.com/yonaskolb/Codability](https://github.com/yonaskolb/Codability)
- [hhfa008/SwiftAI - https://github.com/hhfa008/SwiftAI](https://github.com/hhfa008/SwiftAI)