tl;dr
結論から言うと全然難しくないです。
読み込む時はjsonのキーを指定してintやstringで読み込むか、jsonを丸ごと取得して構造体にunmarshalします。
書き込むときも構造体をmarshalしてSQLに渡すだけです。
テーブルの用意
ユーザ、データベースの作成は割愛します。
$ psql -U test_user test_db
test_db=> CREATE TABLE jojo ( id serial, chapter int, data json );
INSERT INTO jojo VALUES (1, '{ "title": "ファントムブラッド", "character": { "hero": "ジョナサン・ジョースター" } }');
INSERT INTO jojo VALUES (2, '{ "title": "戦闘潮流", "character": { "hero": "ジョセフ・ジョースター" } }');
INSERT INTO jojo VALUES (3, '{ "title": "スターダストクルセイダース", "character": { "hero": "空条承太郎" } }');
このとき、hero
のフィールドを指定してSELECTするには以下のようにします。
test_db=> SELECT data->'character'->>'hero' AS hero FROM jojo;
hero
--------------------------
ジョナサン・ジョースター
ジョセフ・ジョースター
空条承太郎
(3 rows)
GolangでPostgreSQLのJSONデータを扱う
とりあえずデータを読んでみる
PostgreSQL用のドライバが必要なので取得します。
$ go get github.com/lib/pq
JSONのことは意識せず、直接SELECTしてみます。
postgersql.go
package main
import (
"database/sql"
"fmt"
_ "github.com/lib/pq" // 読み込むだけで直接は使わない
)
func main() {
db, _ := sql.Open("postgres", "user=test_user password=password dbname=test_db sslmode=disable")
defer db.Close()
// SELECT(JSONのフィールドを直接指定)
rows, _ := db.Query("SELECT data->'character'->>'hero' as hero FROM jojo;")
//ポインタが先頭より前なので1つ目でも`Next()`が必要
for rows.Next() {
var hero string // 普通にstringとして読める
rows.Scan(&hero)
fmt.Println(hero)
}
}
->
と->>
を使ってフィールドを直接読み込めばJSONであることは特に意識しなくても読み出すことができます。
$ go run postgresql.go
> ジョナサン・ジョースター
> ジョセフ・ジョースター
> 空条承太郎
JSONのデータを読み書きする
さて、本題です。テーブルのスキーマ状態に合わせて構造体を作り、json.Marshal()
、json.Unmarshal()
を使ってデータを扱います。
postgresql.go
package main
import (
"database/sql"
"encoding/json"
"fmt"
"github.com/k0kubun/pp" //見やすさのために利用させていただいてます
_ "github.com/lib/pq"
)
type Jojo struct {
ID int64
Chapter int
JojoData json.RawMessage
}
type JojoData struct {
Title string `json:"title"`
Character Character `json:"character"`
}
type Character struct {
Hero string `json:"hero"`
}
func main() {
db, _ := sql.Open("postgres", "user=test_user password=password dbname=test_db sslmode=disable")
defer db.Close()
// INSERT
insertQuery := "insert into jojo (chapter, data) values($1, $2)"
// 構造体としてデータを作成します
newCharacter := Character{Hero: "東方仗助"}
newData := JojoData{Title: "ダイヤモンドは砕けない", Character: newCharacter}
// JSON化して第4部のデータとして挿入します
d, _ := json.Marshal(newData)
db.Exec(insertQuery, 4, d)
// SELECT
rows, _ := db.Query("SELECT id, chapter, data as hero FROM jojo ORDER BY chapter ASC;")
for rows.Next() {
var jojo Jojo
rows.Scan(&jojo.ID, &jojo.Chapter, &jojo.JojoData)
// 取得したデータを構造体にマッピングします
var d JojoData
json.Unmarshal(jojo.JojoData, &d)
pp.Println(fmt.Sprintf("第%d部", jojo.Chapter))
pp.Println(d)
}
}
実行してみます。
$ go run postgresql.go
"第1部"
main.JojoData{
Title: "ファントムブラッド",
Character: main.Character{
Hero: "ジョナサン・ジョースター",
},
}
"第2部"
main.JojoData{
Title: "戦闘潮流",
Character: main.Character{
Hero: "ジョセフ・ジョースター",
},
}
"第3部"
main.JojoData{
Title: "スターダストクルセイダース",
Character: main.Character{
Hero: "空条承太郎",
},
}
"第4部"
main.JojoData{
Title: "ダイヤモンドは砕けない",
Character: main.Character{
Hero: "東方仗助",
},
}
まとめ
GolangでPostgreSQLのJSONデータ型を読み書きする方法について書きました。
特にJSONだからといって難しいことはなく、普通にJSONのエンコード・デコードをしてあげれば大丈夫でした。実際はgorpやgorm を使うことが多いと思いますが、上の基本を知っていればあまり迷うことはなさそうです。