Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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データ形式との互換性が向上しました。

関連リンク

参考にした情報源リンク