はじめ
最近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