Jsonの読み書きをCodableで簡単に

ちょっとしたコードや設定などですが、忘れがちであらためて調べるのも面倒なので、忘備録的なものです。
順不同で、気づいたものから順にSwift4で使えるコードを載せていきます。

Swift-Tips

Jsonの読み書きをCodableで簡単に

swift4ではCodableを利用することでJsonファイルの読み書きが簡単になりました。

jsonDecoderでJsonファイルのデータを構造体に読み出し
・Codableな構造体を定義しておくこと(通常の構造体にCodable宣言を付加するだけ)
・おまけ:texts配列をCSVに変換する関数texts2CSV()
・初期状態では読み出し元ファイルがないのでデフォルトの設定から作る
・Audioフォルダに音声ファイルを管理するJsonの例

構造体の定義


// Jsonファイルから読み出したデータを保存する構造体
// 構造体内のtexts配列をCSVに変換する関数texts2CSV()
struct Entry: Codable {
    var date:Date
    var title:String
    var memo:String
    var audioFilePath:String
    var audioFileName:String
    var texts:[TextPhrase]
    
    /// カンマ区切りの文字列に変換
    func texts2CSV() -> String {
        var rtnStr = ""
        for i in 0 ..< texts.count {
            let time:String = "\"" + texts[i].time2hhmmss() + "\",\""
            let speaker:String = texts[i].speaker  + "\",\""
            rtnStr += time + speaker + texts[i].text  + "\"\r\n"
        }
        return rtnStr
    }
}

// Jsonファイルから読み出したデータを保存する構造体
// 配列の中身の構造体もCodableで定義しておく
// 構造体内のtopの時間をhh:mm:ssに変換する関数time2hhmmss()
struct TextPhrase: Codable {
    var top:TimeInterval
    var end:TimeInterval?
    var speaker:String
    var text:String
    var comment:String
    
    func time2hhmmss() -> String {
        let formatter = DateComponentsFormatter()
        formatter.unitsStyle = .positional
        formatter.allowedUnits = [.minute,.hour,.second]
        formatter.zeroFormattingBehavior = .pad
        return formatter.string(from: top) ?? ""
    }
}

読み書きするjsonはこんな感じ


[
  {
    "memo" : "",
    "title" : "このアプリの使い方",
    "audioFileName" : "sample.mp3",
    "texts" : [
      {
        "speaker" : "編集部",
        "top" : 0,
        "end" : 7.0,
        "comment" : "Repeat After YOU!",
        "text" : "このアプリは文字起こしを超高速に行うことに特化したアプリです。"
      },
      {
        "speaker" : "編集部",
        "top" : 7.0,
        "end" : 18.0,
        "comment" : "",
        "text" : "録音音声から聞き取った内容を、オウム返しで話して音声認識させることで、キーボードを使わずに超高速に文字起こしができます。"
      } 
   ],
    "audioFilePath" : "sample.mp3",
    "date" : 531674816.56146502
  }
]

Jsonの読み出し


  var entries: [Entry] = []

    func readSavedJson() {
        let jsonPath = liblaryPath + "/" + jsonFileName
        // jsonをファイルから読み出す
        let fm = FileManager()
        let jsonDecoder = JSONDecoder()
        let jsonStr:String


        // ファイルの有無をチェック
        if fm.fileExists(atPath: jsonPath) {
            do {
                jsonStr = try String(contentsOfFile: jsonPath, encoding: .utf8)
            } catch _ {
                return
            }
        } else {
            print("No File : \(jsonPath)")
            //entries = []
            // 初期状態では何もないのでリソースから作る
            if let assetJson = NSDataAsset(name: "originJson") {
                jsonStr = String(data: assetJson.data, encoding: .utf8) ?? ""
                // サンプルファイルをAudioフォルダにコピー
                if let assetAudio = NSDataAsset(name: "sampleAudio") {
                    let audioData = assetAudio.data as NSData
                    let audioFilePath = documentsPath + "/Audio/sample.mp3"
                    let audioFolderPath = documentsPath + "/Audio"
                    //Audioフォルダが無ければ作る
                    var isDirExists : ObjCBool = false
                    fm.fileExists(atPath: audioFolderPath, isDirectory:&isDirExists)
                    if !isDirExists.boolValue {
                        do {
                            try fm.createDirectory(atPath: audioFolderPath, withIntermediateDirectories: true, attributes: nil)
                        } catch let error1 {
                            print("Error createDirectory : \(audioFolderPath) : " + error1.localizedDescription  )
                            return
                        }
                    }
                    // ファイルを書き込む
                    audioData.write(toFile: audioFilePath, atomically: true)
                }
                
            } else {
                entries = [Entry(date:Date(),title:"No Date",memo:"",audioFilePath:"NoAudio.mp4",audioFileName:"NoAudio.mp4",texts:[TextPhrase(top:0,end:nil,speaker:"No One",text:"say something",comment:"no comment")])]
                return
            }
        }
        
        entries = try! jsonDecoder.decode([Entry].self, from: jsonStr.data(using: .utf8)!)

    }

Jsonの書き込み

JSONEncoderで構造体をエンコードして書き込み
・構造体は上で定義したもの


    func saveEntriesToJson() {
        let jsonPath = liblaryPath + "/" + jsonFileName

        let jsonEncoder = JSONEncoder()
        jsonEncoder.outputFormatting = .prettyPrinted
        
        let data = try! jsonEncoder.encode(entries)
        let dataStr = String(data:data,encoding:.utf8)
        
        if dataStr?.isEmpty ?? true { return }
        do {
            // Stringデータをファイルに書き出し
            try dataStr!.write(toFile: jsonPath, atomically: true, encoding: .utf8)
        } catch _ {
            print("Write Error!  File : \(jsonPath)")
        }
    }