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

[インデックス 14727] ファイルの概要

このコミットは、Go言語の標準ライブラリであるencoding/jsonパッケージにおけるJSONタグのパースロジックの改善に関するものです。具体的には、JSONタグがスペースやUnicode文字を含むことができるように拡張され、より柔軟なタグの定義が可能になりました。

コミット

commit 475f3df43fa6f89413bc142c61a5551d3f8bd281
Author: Stéphane Travostino <stephane.travostino@gmail.com>
Date:   Sat Dec 22 13:36:55 2012 -0500

    encoding/json: A JSON tag can be any valid JSON string.
    
    Fixes #3887.
    
    R=golang-dev, daniel.morsing, remyoudompheng, rsc
    CC=golang-dev
    https://golang.org/cl/6997045

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/475f3df43fa6f89413bc142c61a5551d3f8bd281

元コミット内容

encoding/json: A JSON tag can be any valid JSON string.

このコミットは、Goのencoding/jsonパッケージにおいて、JSONタグが任意の有効なJSON文字列であることを許可するように変更します。これにより、タグにスペースやUnicode文字を含めることが可能になります。

変更の背景

この変更は、Goのencoding/jsonパッケージが構造体のフィールドをJSONにエンコード/デコードする際に使用する「JSONタグ」の柔軟性を高めることを目的としています。以前の実装では、JSONタグとして使用できる文字に制限があり、特にスペースや一部のUnicode文字が許可されていませんでした。

コミットメッセージには「Fixes #3887」と記載されており、これはGitHubのIssue 3887を修正するものであることを示唆しています。一般的なソフトウェア開発において、ユーザーからのフィードバックやバグ報告に基づいて機能が改善されることはよくあります。このケースも、おそらくユーザーがJSONタグに特定の文字を使用しようとして問題に直面し、それがIssueとして報告された結果、この修正が導入されたと考えられます。

JSONタグの制限は、特に外部システムとの連携や、特定の命名規則を持つJSONデータ構造を扱う際に問題となる可能性がありました。例えば、スペースを含むキー名を持つJSONデータをGoの構造体にマッピングする場合、Goのタグがそのキー名を正確に表現できないという制約がありました。このコミットは、そのような制約を取り除き、encoding/jsonパッケージの汎用性と実用性を向上させるものです。

前提知識の解説

Go言語のencoding/jsonパッケージ

Go言語の標準ライブラリであるencoding/jsonパッケージは、Goのデータ構造とJSONデータの間でエンコード(GoからJSONへ)およびデコード(JSONからGoへ)を行うための機能を提供します。このパッケージは、WebサービスやAPI開発において、JSON形式のデータを扱う上で不可欠なツールです。

JSONタグ (Struct Tags)

Go言語の構造体(struct)のフィールドには、「タグ(tag)」と呼ばれるメタデータを付与することができます。タグは、バッククォート()で囲まれた文字列で、フィールドの宣言の直後に記述されます。JSONタグは、json:"key_name,omitempty"のような形式で記述され、encoding/json`パッケージが構造体をJSONに変換する際に、フィールド名をどのようにJSONのキー名にマッピングするか、あるいは特定の条件でフィールドを省略するかなどを制御するために使用されます。

例:

type User struct {
    Name  string `json:"full_name"` // JSONでは "full_name" として扱われる
    Email string `json:"email,omitempty"` // 値がゼロ値の場合、JSONから省略される
}

strings.ContainsRune関数

Goのstringsパッケージに含まれるContainsRune関数は、指定された文字列(s)が特定のルーン(Unicodeコードポイント、rune型)を含んでいるかどうかを判定します。このコミットでは、JSONタグとして許可される文字のセットを定義するために使用されています。

Unicodeとルーン

Go言語では、文字列はUTF-8でエンコードされたバイト列として扱われますが、個々の文字は「ルーン(rune)」として表現されます。ルーンはGoの組み込み型であり、Unicodeコードポイントを表すint32のエイリアスです。これにより、Goは多言語対応を容易に行うことができます。

Issue Tracking System (GitHub Issues)

GitHubなどのバージョン管理プラットフォームでは、プロジェクトのバグ報告、機能要望、タスク管理などに「Issue」が使用されます。コミットメッセージに「Fixes #XXXX」と記述することで、そのコミットが特定のIssueを解決したことを関連付け、追跡しやすくします。

技術的詳細

このコミットの主要な変更点は、src/pkg/encoding/json/encode.goファイル内のisValidTag関数のロジックにあります。この関数は、Goの構造体タグとして与えられた文字列が、JSONタグとして有効な文字のみで構成されているかをチェックします。

変更前は、isValidTag関数内で許可される句読点文字のセットが定義されており、その中にスペース文字が含まれていませんでした。

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

この行は、タグ内の各文字cが、指定された文字列リテラルに含まれるいずれかのルーンであるかどうかをチェックしています。この文字列リテラルには、一般的な句読点文字が含まれていますが、スペース( )は含まれていませんでした。

このコミットでは、この文字列リテラルにスペース文字が追加されました。

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

これにより、isValidTag関数はスペース文字を有効なタグ文字として認識するようになります。

また、src/pkg/encoding/json/tagkey_test.goファイルには、この変更を検証するための新しいテストケースが追加されました。

  1. spaceTag構造体: JSONタグにスペースを含むフィールドを持つ構造体です。
    type spaceTag struct {
        Q string `json:"With space"`
    }
    
  2. unicodeTag構造体: JSONタグにUnicode文字(この場合はギリシャ語の「Ελλάδα」)を含むフィールドを持つ構造体です。
    type unicodeTag struct {
        W string `json:"Ελλάδα"`
    }
    

これらの新しい構造体に対応するテストデータがstructTagObjectKeyTestsスライスに追加され、TestStructTagObjectKey関数によって、スペースやUnicode文字を含むJSONタグが正しくパースされ、期待されるキー名が抽出されることが検証されます。

この変更により、encoding/jsonパッケージは、より広範なJSONキー名に対応できるようになり、国際化されたアプリケーションや、既存のJSONスキーマとの互換性を高める上で重要な改善となります。

コアとなるコードの変更箇所

src/pkg/encoding/json/encode.go

--- a/src/pkg/encoding/json/encode.go
+++ b/src/pkg/encoding/json/encode.go
@@ -437,7 +437,7 @@ func isValidTag(s string) bool {
 	}\n \tfor _, c := range s {\n \t\tswitch {\n-\t\tcase strings.ContainsRune(\"!#$%&()*+-./:<=>?@[]^_{|}~\", c):\n+\t\tcase strings.ContainsRune(\"!#$%&()*+-./:<=>?@[]^_{|}~ \", c):\n \t\t\t// Backslash and quote chars are reserved, but\n \t\t\t// otherwise any punctuation chars are allowed\n \t\t\t// in a tag name.\n```

### `src/pkg/encoding/json/tagkey_test.go`

```diff
--- a/src/pkg/encoding/json/tagkey_test.go
+++ b/src/pkg/encoding/json/tagkey_test.go
@@ -60,6 +60,14 @@ type badCodeTag struct {
 	Z string `json:\" !\\\"#&\'()*+,.\"`\n }\n \n+type spaceTag struct {\n+\tQ string `json:\"With space\"`\n+}\n+\n+type unicodeTag struct {\n+\tW string `json:\"Ελλάδα\"`\n+}\n+\n var structTagObjectKeyTests = []struct {\n \traw   interface{}\n \tvalue string\n@@ -78,6 +86,8 @@ var structTagObjectKeyTests = []struct {\n \t{badCodeTag{\"Reliable Man\"}, \"Reliable Man\", \"Z\"},\n \t{percentSlashTag{\"brut\"}, \"brut\", \"text/html%\"},\n \t{punctuationTag{\"Union Rags\"}, \"Union Rags\", \"!#$%&()*+-./:<=>?@[]^_{|}~\"},\n+\t{spaceTag{\"Perreddu\"}, \"Perreddu\", \"With space\"},\n+\t{unicodeTag{\"Loukanikos\"}, \"Loukanikos\", \"Ελλάδα\"},\n }\n \n func TestStructTagObjectKey(t *testing.T) {\n```

## コアとなるコードの解説

### `src/pkg/encoding/json/encode.go`の変更

このファイルでは、`isValidTag`関数の内部で、JSONタグとして許可される文字のセットを定義している文字列リテラルに、スペース文字(` `)が追加されました。

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

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

この小さな変更により、`isValidTag`関数は、JSONタグ文字列内にスペースが含まれていても、それを有効なタグとして認識するようになります。これにより、例えば`json:"my field"`のようなタグが正しく処理されるようになります。

### `src/pkg/encoding/json/tagkey_test.go`の変更

このテストファイルでは、新しいテストケースが追加され、`isValidTag`関数の変更が正しく機能することを検証しています。

1.  **`spaceTag`構造体とテストケースの追加**:
    `type spaceTag struct { Q string `json:"With space"` }`
    この構造体は、JSONタグにスペースを含むフィールド`Q`を定義しています。対応するテストケース`{spaceTag{"Perreddu"}, "Perreddu", "With space"}`が追加され、`"With space"`というタグが正しくパースされ、JSONキーとして認識されることを確認します。

2.  **`unicodeTag`構造体とテストケースの追加**:
    `type unicodeTag struct { W string `json:"Ελλάδα"` }`
    この構造体は、JSONタグにUnicode文字(ギリシャ語の「Ελλάδα」)を含むフィールド`W`を定義しています。対応するテストケース`{unicodeTag{"Loukanikos"}, "Loukanikos", "Ελλάδα"}`が追加され、Unicode文字を含むタグが正しく処理されることを確認します。

これらのテストケースは、`encoding/json`パッケージがJSONタグのパースにおいて、スペースやUnicode文字を適切に扱えるようになったことを保証します。これは、GoのJSON処理がより堅牢で国際化に対応したものになったことを意味します。

## 関連リンク

*   Go言語の公式ドキュメント: [https://golang.org/](https://golang.org/)
*   Goの`encoding/json`パッケージのドキュメント: [https://pkg.go.dev/encoding/json](https://pkg.go.dev/encoding/json)
*   このコミットのGo Gerrit Code Reviewリンク: [https://golang.org/cl/6997045](https://golang.org/cl/6997045)

## 参考にした情報源リンク

*   GitHubのコミットページ: [https://github.com/golang/go/commit/475f3df43fa6f89413bc142c61a5551d3f8bd281](https://github.com/golang/go/commit/475f3df43fa6f89413bc142c61a5551d3f8bd281)
*   Go言語の構造体タグに関する一般的な情報源 (例: Go by Example - Structs): [https://gobyexample.com/structs](https://gobyexample.com/structs) (一般的なGoの構造体タグの概念理解のため)
*   `strings.ContainsRune`関数のドキュメント: [https://pkg.go.dev/strings#ContainsRune](https://pkg.go.dev/strings#ContainsRune)
*   Go言語におけるUnicodeとルーンに関する情報源 (例: Go Blog - The Go Programming Language and Unicode): [https://go.dev/blog/strings](https://go.dev/blog/strings) (Goにおける文字列とUnicodeの扱いの理解のため)
*   Issue 3887の具体的な内容はWeb検索では特定できませんでしたが、コミットメッセージからその存在が示唆されています。