[インデックス 12955] ファイルの概要
このコミットは、Go言語の標準ライブラリである encoding/json
パッケージにおいて、JSON構造体タグ名に句読点文字の使用を許可するように変更を加えるものです。具体的には、バックスラッシュと引用符を除く、ほとんどの句読点文字がタグ名として有効になります。これにより、より柔軟なJSONタグの定義が可能となり、特定のユースケース(例: @
を含むタグ名)に対応できるようになります。
コミット
- コミットハッシュ:
52f122d72ee3fab46603ccd76259d1f9db50be7b
- Author: Bobby Powers bobbypowers@gmail.com
- Date: Wed Apr 25 14:33:33 2012 +1000
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/52f112d72ee3fab46603ccd76259d1f9db50be7b
元コミット内容
encoding/json: allow punctuation in tag names
everything except backslash and the quote chars is fair game.
Fixes #3546.
R=rsc, r
CC=golang-dev
https://golang.org/cl/6048047
変更の背景
この変更は、Go言語の encoding/json
パッケージにおけるJSON構造体タグの命名規則に関する制限を緩和するために行われました。以前のバージョンでは、JSONタグ名に使用できる文字が厳しく制限されており、特に @
などの一部の句読点文字が許可されていませんでした。
この問題は、GitHub Issue #3546「encoding/json: '@' not allowed in tag name」として報告されました。このIssueでは、ユーザーが json:"@source"
のようなタグを使用しようとした際に、encoding/json
パッケージがこれを無効なタグとして扱い、正しくエンコード/デコードできないという問題が指摘されていました。
多くの既存のJSONデータ形式やAPIでは、フィールド名に句読点文字(特に @
や -
など)を含むことが一般的です。Goの encoding/json
パッケージがこれらの文字をサポートしないことは、Goアプリケーションが既存のJSONデータと連携する際の障壁となっていました。このコミットは、このような互換性の問題を解消し、開発者がより柔軟にJSONタグを定義できるようにすることを目的としています。
前提知識の解説
Go言語の encoding/json
パッケージ
encoding/json
パッケージは、Go言語でJSONデータをエンコード(Goのデータ構造からJSONへ変換)およびデコード(JSONからGoのデータ構造へ変換)するための標準ライブラリです。このパッケージは、Goの構造体とJSONオブジェクト間のマッピングを自動的に処理する機能を提供します。
Goの構造体タグ (Struct Tags)
Goの構造体フィールドには「タグ」と呼ばれるメタデータを付与することができます。タグは、バッククォート (`
) で囲まれた文字列で、フィールドの宣言の直後に記述されます。これらのタグは、リフレクションAPIを通じて実行時にアクセスでき、encoding/json
のようなパッケージが、構造体フィールドとJSONフィールドの名前のマッピング、エンコード/デコード時の挙動の制御などに利用します。
例えば、json:"field_name"
のようにタグを付けることで、Goの構造体フィールド名と異なるJSONフィールド名を指定できます。
type User struct {
Name string `json:"user_name"`
Age int `json:"age,omitempty"`
}
この例では、Name
フィールドはJSONでは user_name
として扱われ、Age
フィールドはJSONでは age
として扱われ、値がゼロ値の場合は省略されます。
JSONタグ名における「タグ名」とは
json:"key"
の形式において、key
の部分が「タグ名」に相当します。このタグ名が、JSONオブジェクトのキーとして使用されます。このコミットの変更は、この key
の部分に使用できる文字の範囲を広げるものです。
技術的詳細
このコミットの主要な変更点は、src/pkg/encoding/json/encode.go
ファイル内の isValidTag
関数にあります。この関数は、JSON構造体タグとして指定された文字列が有効なタグ名であるかどうかを検証する役割を担っています。
変更前の isValidTag
関数
変更前の isValidTag
関数は、タグ名に使用できる句読点文字を非常に限定していました。具体的には、$
、-
、_
、/
、%
のみが許可されており、それ以外の句読点文字(例: @
, !
, #
など)は無効と判断されていました。
func isValidTag(s string) bool {
if s == "" {
return false
}
for _, c := range s {
switch c {
case '$', '-', '_', '/', '%':
// Acceptable
default:
if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
return false
}
}
}
return true
}
このロジックでは、$
、-
、_
、/
、%
以外の文字が句読点であった場合、unicode.IsLetter(c)
も unicode.IsDigit(c)
も false
を返すため、return false
が実行され、タグが無効と判断されていました。
変更後の isValidTag
関数
変更後の isValidTag
関数では、strings
パッケージの ContainsRune
関数を利用して、許可される句読点文字のセットを大幅に拡張しています。新しいロジックでは、バックスラッシュ (\
) と引用符 ("
) を除く、ほとんどの一般的な句読点文字がタグ名として許可されます。
func isValidTag(s string) bool {
if s == "" {
return false
}
for _, c := range s {
switch {
case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~", c):
// Backslash and quote chars are reserved, but
// otherwise any punctuation chars are allowed
// in a tag name.
default:
if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
return false
}
}
}
return true
}
この変更により、!
、#
、%
、&
、(
、)
、*
、+
、-
、.
、/
、:
、<
、=
、>
、?
、@
、[
、]
、^
、_
、{
、|
、}
、~
といった文字がタグ名として使用できるようになりました。バックスラッシュと引用符が除外されているのは、これらがGoの文字列リテラルやJSONの構文において特別な意味を持つためです。
また、この変更に合わせて、src/pkg/encoding/json/tagkey_test.go
に新しいテストケース punctuationTag
が追加され、新しい句読点文字が正しくタグ名として認識されることを検証しています。
コアとなるコードの変更箇所
diff --git a/src/pkg/encoding/json/encode.go b/src/pkg/encoding/json/encode.go
index 14957b8487..842672c397 100644
--- a/src/pkg/encoding/json/encode.go
+++ b/src/pkg/encoding/json/encode.go
@@ -17,6 +17,7 @@ import (
"runtime"
"sort"
"strconv"
+ "strings"
"sync"
"unicode"
"unicode/utf8"
@@ -415,9 +416,11 @@ func isValidTag(s string) bool {
if s == "" {
return false
}
for _, c := range s {
- switch c {
- case '$', '-', '_', '/', '%':
- // Acceptable
+ switch {
+ case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~", c):
+ // Backslash and quote chars are reserved, but
+ // otherwise any punctuation chars are allowed
+ // in a tag name.
default:
if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
return false
diff --git a/src/pkg/encoding/json/tagkey_test.go b/src/pkg/encoding/json/tagkey_test.go
index bba5730353..da8b12bd8f 100644
--- a/src/pkg/encoding/json/tagkey_test.go
+++ b/src/pkg/encoding/json/tagkey_test.go
@@ -40,6 +40,10 @@ type percentSlashTag struct {
V string `json:"text/html%"` // http://golang.org/issue/2718
}
+type punctuationTag struct {
+ V string `json:"!#$%&()*+-./:<=>?@[]^_{|}~"` // http://golang.org/issue/3546
+}
+
type emptyTag struct {
W string
}
@@ -73,6 +77,7 @@ var structTagObjectKeyTests = []struct {
{badFormatTag{"Orfevre"}, "Orfevre", "Y"},
{badCodeTag{"Reliable Man"}, "Reliable Man", "Z"},
{percentSlashTag{"brut"}, "brut", "text/html%"},
+ {punctuationTag{"Union Rags"}, "Union Rags", "!#$%&()*+-./:<=>?@[]^_{|}~"},
}
func TestStructTagObjectKey(t *testing.T) {
コアとなるコードの解説
src/pkg/encoding/json/encode.go
の変更
import "strings"
の追加:strings.ContainsRune
関数を使用するために、strings
パッケージがインポートされました。isValidTag
関数のロジック変更:- 以前の
switch c
文がswitch
文に変わり、より複雑な条件分岐を扱えるようになりました。 case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~", c):
の行が追加されました。これは、現在の文字c
が指定された句読点文字の文字列に含まれているかどうかをチェックします。含まれていれば、その文字は有効なタグ文字として許可されます。- コメント
// Backslash and quote chars are reserved, but // otherwise any punctuation chars are allowed // in a tag name.
が追加され、この変更の意図が明確にされています。
- 以前の
この変更により、isValidTag
関数は、英数字に加えて、指定された句読点文字も有効なタグ文字として認識するようになります。
src/pkg/encoding/json/tagkey_test.go
の変更
-
punctuationTag
構造体の追加:type punctuationTag struct { V string `json:"!#$%&()*+-./:<=>?@[]^_{|}~"` // http://golang.org/issue/3546 }
この新しい構造体は、新しい
isValidTag
ロジックで許可されるようになったすべての句読点文字を含むJSONタグを持つフィールドV
を定義しています。これは、Issue #3546で報告された問題が解決されたことを直接テストするためのものです。 -
structTagObjectKeyTests
スライスへのテストケースの追加:{punctuationTag{"Union Rags"}, "Union Rags", "!#$%&()*+-./:<=>?@[]^_{|}~"},
structTagObjectKeyTests
は、JSONタグのエンコード/デコードが正しく行われるかを検証するためのテストデータセットです。ここに追加されたエントリは、punctuationTag
構造体を使用して、句読点を含むタグ名が正しく処理されることを確認します。"Union Rags"
はテスト用の値、"!#$%&()*+-./:<=>?@[]^_{|}~"
は期待されるJSONキーです。
これらの変更により、encoding/json
パッケージは、より多様なJSONタグ名をサポートできるようになり、既存のJSONデータ形式との互換性が向上しました。
関連リンク
- GitHub Issue #3546: https://github.com/golang/go/issues/3546
- Go CL (Change List) 6048047: https://golang.org/cl/6048047
参考にした情報源リンク
- https://github.com/golang/go/commit/52f122d72ee3fab46603ccd76259d1f9db50be7b
- https://github.com/golang/go/issues/3546
- Go言語の
encoding/json
パッケージのドキュメント (一般的な知識として) - Go言語の構造体タグに関するドキュメント (一般的な知識として)