LoginSignup
29
16

More than 5 years have passed since last update.

GoでJSONのnullをいい感じに扱いたい

Last updated at Posted at 2017-11-03

GoでJSONのnullをいい感じに扱いたいことがあるとします。しかしGoではnullを扱うのは容易ではありません。

Goにはnilが存在しますが、これはポインタ型でしか使えません。よってintstring型では使用できません。Goはintstring型は初期化しなかった場合、ゼロ値に初期化されます。intのゼロ値は0、stringのゼロ値は空文字列です。そのためJSONのnullをGoで扱おうとした場合、Goのゼロ値との区別ができません。

同じ問題はSQLでもあります。nullが存在するカラムから値を取得した際にnullとGoのゼロ値を区別する必要があります。そこでGoのdatabase/sqlではNullStringのようなstructが定義されています。

type NullString struct {
    String string
    Valid  bool // Valid is true if String is not NULL
}

Validの真偽値を確認することで空文字列とnullを区別することができます。これと同じことをJSONでやるにはどうしたらよいでしょうか。

json.Marshaljson.Unmarshalの動きを変更するには以下のインタフェースを実装します。

interface.go
type Unmarshaler interface {
    UnmarshalJSON([]byte) error
}

type Marshaler interface {
    MarshalJSON() ([]byte, error)
}

そこで以下のような実装をしてみました。

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
)

type NullSegments struct {
    Segments Segments
    Valid    bool // Valid is true if Segments is not NULL
}

type Range struct {
    From int `json:"from"`
    To   int `json:"to"`
}

type Segments struct {
    Status int   `json:"status"`
    Range  Range `json:"range"`
}

type Settings struct {
    Segments NullSegments `json:"segments"`
}

var nullLiteral = []byte("null")

func (s *NullSegments) UnmarshalJSON(b []byte) error {
    if bytes.Equal(b, nullLiteral) {
        return nil
    }

    err := json.Unmarshal(b, &s.Segments)
    if err == nil {
        s.Valid = true
        return nil
    }

    return err
}

func (s NullSegments) MarshalJSON() ([]byte, error) {
    if s.Valid {
        return json.Marshal(s.Segments)
    } else {
        return nullLiteral, nil
    }
}

func main() {
    b := []byte(`{"segments": {"status": 1, "range": {"from": 10, "to": 20}}}`)
    // b := []byte(`{"segments": null }`)

    s := &Settings{}

    json.Unmarshal(b, s)

    bb, _ := json.Marshal(s)

    fmt.Println(string(bb))
}

注意点としては[]bytestringにキャストするのはコストが高いのでbytes.Equalなどのメソッドを使います。

bool型のゼロ値はfalseなのでnullが来たときは何もしなくて大丈夫です。null以外が来た場合にValidをtrueにする必要があります。

29
16
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
29
16