[golang] 続・NullStringを綺麗にJSONエンコードするためのちょっとましになったやり方

前回の丁稚

こちらの通り

続・目的

タイトルの通り。
sql.NullStringはNullableなカラムを引き受けてくれる型だが、そのままJSONに引き渡してもオブジェクトとして出力されてしまう。
これを綺麗にJSONとして出力したかった。

綺麗でない例は以下のとおり

before

[{"id":1,"string":{"String":"aaa","Valid":true}},{"id":2,"string":{"String":"","Valid":false}}]

after

[{"id":1,"string":"aaa"},{"id":2,"string":""}]

方法

Null*型をJSONに引き渡して好きなかたちで出力したい場合、MarshalJSONを自分で実装すれば良い。問題はSQL実行後のScanでアサインされないことだった。

前回はここで糞長いコードを書いていた。

よくわからなかったので、値をバインドする部分はコアのコードを丸コピして持ってきたfuncをコール。つまり手抜きである。

今回は前回のコードを少し綺麗にした。

実装

ソース

一旦ベタで貼る。短縮できる方法は試してみるが、考えるのが面倒だったのでconverterそのままコピペした。完全に良くない。

// localtype パッケージ
// アプリケーションローカルの型はここに格納したい
package localtype

import (
 "database/sql"
 "database/sql/driver"
 "encoding/json"
)

// type OptionalString
// sql.NullStringのラッパー
type OptionalString sql.NullString

// func (o *OptionalString) Scan(value interface{}) error
// Implements Scan for settting value from database Row
func (o *OptionalString) Scan(value interface{}) error {
 if value == nil {
  o.String, o.Valid = "", false
  return nil
 } else {
  o.String, o.Valid = string(value.([]byte)), true
  return nil
 }
 return nil
}

// func Convert(s string) OptionalString
// convert OptionalString from simple string
func ConvertOptionalString(s string) *OptionalString {
 var valided = false
 if len(s) > 0 {
  valided = true
 }
 return &OptionalString{
  Valid:  valided,
  String: s,
 }
}

// func (o *OptionalString) Convert() *sql.NullString
// convert from OptionalString to NullString
func (o *OptionalString) Convert() *sql.NullString {
 return &sql.NullString{
  Valid:  o.Valid,
  String: o.String,
 }
}

// func (o *OptionalString) Value() (driver.Value, error)
// Value is needed such as func Scan
func (o *OptionalString) Value() (driver.Value, error) {
 if o.Valid {
  return o.String, nil
 }
 return "", nil
}

// func (o *OptionalString) GetString() string
// return string for safe
// ofcourse, you're ok to access OptionalString.String directly
func (o *OptionalString) SafeString() string {
 if o.Valid {
  return o.String
 }
 return ""
}

// func (o *OptionalString) MarshalJSON() ([]byte, error)
// convert to Json value
func (o *OptionalString) MarshalJSON() ([]byte, error) {
 if o.Valid {
  return json.Marshal(o.String)
 }
 return json.Marshal("")
}

// func (o *OptionalString) UnmarshalJSON(data []byte) error
// convert from string in Json
func (o *OptionalString) UnmarshalJSON(data []byte) error {
 ns := ConvertOptionalString(string(data)).Convert()
 d, err := json.Marshal(ns)
 if err != nil {
  return err
 }
 return json.Unmarshal(d, &sql.NullString{})
}

プラス、UnmarshalJSONを実装したので、JSONから構造体に戻すのもdecoderで実行可能である。
しかし問題はまだあって、このUnmarshalJSONの実装が汚いということである。
悩みは尽きない。

このブログの人気の投稿

2016年にgoを使ったのでまとめ

採用とは何か

エンジニアの妻になってしまった我が妻には感謝している