BASEプロダクトチームブログ

ネットショップ作成サービス「BASE ( https://thebase.in )」、ショッピングアプリ「BASE ( https://thebase.in/sp )」のプロダクトチームによるブログです。

Go 1.16 細かいけどためになった encoding/json パッケージの Minor change

こんにちは。BASE BANK 株式会社 Dev Division にて、Engineering Manager をしている東口(@hgsgtk)です。

BASE では @budougumi0617 さんが主催となって Go のコードリーディング会を行っています。昨年は『私がGoのソースコードを読むときのTips』にてその様子を少し紹介いただいていました。

その後も隔週の毎月 2 回ペースでコードリーディング会を継続しています。2020 年 12 月以降は前述したブログをきっかけに興味を持っていただいた@dice_zuさんやpo3rinさんにもゲスト参加いただき、ゆるゆると継続しています。

当エントリではその中で話題に上がった Go 1.16 の機能についてご紹介いたします。

TL;DR

  • 構造体フィールドタグ json オブジェクト名の中でセミコロン(;)が許可されました
  • たとえば json:"hoge;hoge" のように ; が JSON オブジェクト名 に含まれる場合も json.Marshal / json.Unmarshal 可能となりました
  • JSONの仕様を定義したRFC7159によるとセミコロンは文字種として valid であるため
  • 社内で開催中のコードリーディング会はゲスト大歓迎です。興味がある方は身近な社員の方か @hgsgtk にご相談ください

Go 1.16 の Minor changes

Go 1.16 の Release Notes にて 1.15 からの変更点について見ることが出来ます。

golang.org

私は毎回 Core library の Minor changes to the library を眺めて気になる PR のコードを読んでみるのが好きです。

その中で、コードリーディング会までネタにあげさせていただいたのが encoding/json パッケージの変更点です。

f:id:khigashigashi:20210331150813p:plain
Go 1.16 における Minor changes
https://golang.org/doc/go1.16#encoding/json

The json struct field tags understood by Marshal, Unmarshal, and related functionality now permit semicolon characters within a JSON object name for a Go struct field.

どういった内容なのか、当日参加者で 15 分程度読んだ内容を皆様にご共有いたします。

encoding/json パッケージの Minor changes

構造体フィールドタグ json オブジェクト名の中でセミコロン(;)が許可されました。具体的には次のようなサンプルコードです。

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    encoded := []byte(`{";": "World!"}`) // セミコロンがKeyのJSON Object
    type MyObject struct {
        Hello string `json:";"`
    }
    var decoded MyObject
    if err := json.Unmarshal(encoded, &decoded); err != nil {
        fmt.Println(err)
        return
    }
    fmt.Printf("%+v", decoded) // Output: {Hello:World!}
}

https://play.golang.org/p/3HUq4N66AK5

変更の背景

「セミコロンが JSON オブジェクトの Key にはいっているものを扱うユースケースがあまり想像つかないね」という話をコードリーディング会ではしていました。どういった背景でこの変更が行われたのでしょうか。起点となった Issue を見てみましょう。

f:id:khigashigashi:20210331150841p:plain
issue: encoding/json: does not recognise semicolon as a valid field name
https://github.com/golang/go/issues/39189

この変更の背景根拠となったのは JSON 仕様を規定している RFC7159 のようです。

So pretty much we check JSON spec (RFC-7159) for validity on our "bug" and it seems to us that the spec would treat a semicolon as a normal character.

Go内部実装における変更点

ここで行われた内部実装の変更は次の CL から確認できます。

https://go-review.googlesource.com/c/go/+/234818

f:id:khigashigashi:20210331150919p:plain
src/encoding/json/encode.go に入った修正 
isValidTagというメソッドにて tag の正当性を検証するのですが、その内部での文字種ルールに;を追加しています。

strings.ContainsRune("!#$%&()*+-./:;<=>?@[]^_{|}~ ", c):

余談 Go 2structured tag という提案

Go コードリーディング会の雑談のなかで Go2 のプロポーザルで structured tags というものが提案されていることを知りました。

github.com

現在の構造タグのフォーマットは文字列リテラルですが、この Issue の提案およびディスカッションの結果、具体例として次のようなコードを記述するような機能提案となっています。

package mypackage

import json
import sqlx

type MyStruct struct {
      // [] 内にstruct定義する    
      Value      string [json.Rules{Name: "value"}, sqlx.Name("value")]
      PrivateKey []byte [json.Rules{Ignore: true}]
}

個人的には、文字列リテラルで記述する方法と比較した際に、記述ミス時に気が付きやすい点が嬉しいと思いました。実行時エラーや無視されてしまうのではなくユーザーがスペルミスした際にコンパイルエラーになります。

Issue 内での会話は 2020 年 1 月ごろに落ち着いていますが、「そういうアイデアがあるんだな」と頭の片隅に入れておくといいかもしれません。

おわりに

かんたんにですが、encoding/json の minor changes について紹介しました。Go の内部コードリーディングのはじめの一歩として身近な標準ライブラリのちょっとした変更を追ってみると、その周辺コードもちょっとしれたりしておすすめです。

社内で開催中のコードリーディング会はゲスト大歓迎です。興味がある方は身近な社員か @hgsgtk にご相談ください。

こちらも余談ですが、BASE BANK 株式会社は絶賛採用募集中です!もし、「ちょっと興味あるな〜」とおもったら、Go 1.16 の話とか現場での Go 開発についてワイワイはなしましょう!

herp.careers