Posted at

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

More than 3 years have passed since last update.


はじめ

最近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