LoginSignup
21
14

More than 5 years have passed since last update.

[golang]null許可DBカラムをjson変換する

Posted at

はじめ

最近golangを書き始めています。
JSONを返却するAPIを書いている時に、NULL許可カラムの扱いに困りました。

そのAPIはどんなことやってたの?

  • DBからデータ取得
  • structにマッピング
  • json変換してレスポンス返却

はい、きっとよくあるやつです。

何がどう困ったか

userテーブルでmiddle_nameカラムはNULL許可で、マッピングさせる structは、sample1のように定義しました。

id          `int`
first_name  `varchar(255)`
middle_name `varchar(255) DEFAULT NULL`
last_name   `varchar(255)`
sample1.go
type User struct {
    Id uint32 `json:"id"`
    FirstName string `json:"first_name"`
    MiddleName string `json:"middle_name"`
    LastName string `json:"last_name"`
}

すると、DBのMiddleNameカラムデータがNULLの時に、string型にマッピングできません。

sql.NullString型にする

sample2.go
type User struct {
    Id uint32 `json:"id"`
    FirstName string `json:"first_name"`
    MiddleName sql.NullString `json:"middle_name"`
    LastName string `json:"last_name"`
}

goのsqlパッケージにNullStringってのがあって、
DBのNULLデータをいい感じにstructにマッピングしてくれます。 やったぜ!

あとは、json.Marshallでjson変換するだけ♪

json変換した結果、sql.NullString型自体がjson化されていて、(´・ω・`)ガッカリ。

unexpected.go
{
   "id" : 1,
   "first_name" : "Dennis",
   "middle_name" : { "String" : "", "Valid" : false }, 
   "last_name" : "Suratna"
}

expected.go
{
   "id" : 1,
   "first_name" : "Dennis",
   "middle_name" : null, 
   "last_name" : "Suratna"
}

null値を変換してくれるライブラリーを使う

gopkg.in/guregu/null.v3
nullとzeroというパッケージがあるらしく、jsonのmarshalがかかると
null -> 0 or 空文字("") に変換してくれるらしい。

sampleコード書いてみました。

sample.go
package main

import (
    null "gopkg.in/guregu/null.v3"
    zero "gopkg.in/guregu/null.v3/zero"
    "database/sql"
    "encoding/json"
    "fmt"
)

type DBUser struct {
    Id uint32 `json:"id"`
    FirstName string `json:"first_name"`
    MiddleName sql.NullString `json:"middle_name"`
    LastName string `json:"last_name"`
    Salary sql.NullInt64 `json:"salary"`
}

type JsonNullUser struct {
    Id uint32 `json:"id"`
    FirstName string `json:"first_name"`
    MiddleName null.String `json:"middle_name"`
    LastName string `json:"last_name"`
    Salary null.Int `json:"salary"`
}

type JsonZeroUser struct {
    Id uint32 `json:"id"`
    FirstName string `json:"first_name"`
    MiddleName zero.String `json:"middle_name"`
    LastName string `json:"last_name"`
    Salary zero.Int `json:"salary"`
}

func main() {

    // --- sql.nullString
    user1 := &DBUser {
        Id: 1,
        FirstName: "Min",
        LastName: "Oh",
    }
    user1Json, _ := json.Marshal(user1)
    fmt.Println(string(user1Json)) // (1)


    // --- null.String, null.Int
    user2 := &JsonNullUser{
        Id: 2,
        FirstName: "Mark",
        LastName: "Parker",
    }

    user2Json, _ := json.Marshal(user2)
    fmt.Println(string(user2Json)) // (2)


    user21 := &JsonNullUser{
        Id: 21,
        FirstName: "Mark",
        MiddleName: null.NewString("", true),
        LastName: "Parker",
        Salary: null.NewInt(0, true),
    }

    user21Json, _ := json.Marshal(user21)
    fmt.Println(string(user21Json)) // (3)

    user3 := &JsonNullUser{
        Id: 3,
        FirstName: "Jack",
        MiddleName: null.NewString("Junior", true),
        LastName: "Paul",
        Salary: null.NewInt(30, true),
    }

    user3Json, _ := json.Marshal(user3)
    fmt.Println(string(user3Json)) // (4)

    // --- zero.String, zeroInt
    user4 := &JsonZeroUser{
        Id: 4,
        FirstName: "Mark",
        LastName: "Parker",
    }
    user4Json, _ := json.Marshal(user4)
    fmt.Println(string(user4Json)) // (5)

    user41 := &JsonZeroUser{
        Id: 41,
        FirstName: "Mark",
        MiddleName: zero.NewString("", true),
        LastName: "Parker",
        Salary: zero.NewInt(0, true),
    }
    user41Json, _ := json.Marshal(user41)
    fmt.Println(string(user41Json)) // (6)

    user5 := &JsonZeroUser{
        Id: 5,
        FirstName: "Jack",
        MiddleName: zero.NewString("Junior", true),
        LastName: "Paul",
        Salary: zero.NewInt(30, true),
    }

    user5Json, _ := json.Marshal(user5)
    fmt.Println(string(user5Json)) //  (7)

実行結果

(1) {"id":1,"first_name":"Min","middle_name":{"String":"","Valid":false},"last_name":"Oh","salary":{"Int64":0,"Valid":false}}
(2) {"id":2,"first_name":"Mark","middle_name":null,"last_name":"Parker","salary":null}
(3) {"id":21,"first_name":"Mark","middle_name":"","last_name":"Parker","salary":0}
(4) {"id":3,"first_name":"Jack","middle_name":"Junior","last_name":"Paul","salary":30}
(5) {"id":4,"first_name":"Mark","middle_name":"","last_name":"Parker","salary":0}
(6) {"id":41,"first_name":"Mark","middle_name":"","last_name":"Parker","salary":0}
(7) {"id":5,"first_name":"Jack","middle_name":"Junior","last_name":"Paul","salary":30}

今回は、zeroパッケージは不要でnullパッケージだけで用が足りそうです。

参考

http://dennissuratna.com/marshalling-nullable-string-db-value-to-json-in-go/
http://qiita.com/guregu/items/726f34f80de4e9ba7539#guregunull

21
14
1

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
21
14