2021年Goアドベントカレンダー 4日目の記事です。
encoding/json
パッケージには Marshaler/Unmarshaler インターフェイスがあります。
インターフェイスの実装を満たす構造体であれば、jsonをGoのデータへマッピングすることや、jsonデータをシリアライズして出力することが出来ます。
type Unmarshaler interface {
UnmarshalJSON([]byte) error
}
type Marshaler interface {
MarshalJSON() ([]byte, error)
}
UnmarshalJSON
は、インプットの json データをハンドルして Go の型へ変換したい時、
MarshalJSON
は、Goの構造体などを json データへシリアライズして出力する際などに利用できます。
MarshalJSON / UnmarshalJSON の実装例
下記のような型を定義して、実装例を示してみたいと思います。
type Bitfield uint8
const (
x uint8 = 1 << iota
w
r
)
Bitfield
に以下のようなメソッドを実装すれば、json配列として表現することが出来ます。
-
UnmarshalJSON
では、論理和を行いインプットを処理する -
MarshalJSON
では、論理積により判定を行う
func (b *Bitfield) UnmarshalJSON(byt []byte) error {
var items []string
if err := json.Unmarshal(byt, &items); err != nil {
return err
}
for _, s := range items {
switch strings.ToLower(s) {
case "x":
*b |= Bitfield(x)
case "w":
*b |= Bitfield(w)
case "r":
*b |= Bitfield(r)
default:
}
}
return nil
}
func (b Bitfield) MarshalJSON() ([]byte, error) {
arr := []string{}
if uint8(b)&x != 0 {
arr = append(arr, "x")
}
if uint8(b)&w != 0 {
arr = append(arr, "w")
}
if uint8(b)&r != 0 {
arr = append(arr, "r")
}
return json.Marshal(arr)
}
出力例
https://go.dev/play/p/w-dIYugBrO9
func main() {
blobs := []string{`["x", "w","r"]`, `["x","r"]`, `["r"]`, `[]`}
for _, blob := range blobs {
var b Bitfield
if err := json.Unmarshal([]byte(blob), &b); err != nil {
log.Fatal(err)
}
byte, err := json.Marshal(b)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%d: %s\n", b, byte)
}
}
7: ["x","x", "w","r"]
5: ["x","r"]
4: ["r"]
0: []