LoginSignup
30
25

More than 5 years have passed since last update.

GolangでPostgreSQLのJSONデータ型を読み書きする

Last updated at Posted at 2016-02-20

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のエンコード・デコードをしてあげれば大丈夫でした。実際はgorpgorm を使うことが多いと思いますが、上の基本を知っていればあまり迷うことはなさそうです。

30
25
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
25