[インデックス 1325] ファイルの概要
このコミットは、Go言語の標準ライブラリに初期のJSON(JavaScript Object Notation)ライブラリを追加するものです。Go言語がまだ公開される前の2008年12月に行われたもので、現在のencoding/json
パッケージの原型となる機能が導入されています。このライブラリは、JSONデータのパース、Goのデータ構造へのアンマーシャリング、および汎用的なJSON表現の操作を提供します。
コミット
commit 793a6effcf58e2739e3053ad3199464e87eb5a58
Author: Russ Cox <rsc@golang.org>
Date: Thu Dec 11 12:25:58 2008 -0800
add JSON library
R=r
DELTA=1127 (1127 added, 0 deleted, 0 changed)
OCL=20975
CL=20983
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/793a6effcf58e2739e3053ad3199464e87eb5a58
元コミット内容
add JSON library
変更の背景
このコミットが行われた2008年当時、Go言語はまだ一般に公開されていませんでした。しかし、Webサービスや分散システムを構築する上で、JSONはデータ交換フォーマットとして既に広く普及していました。Go言語がこれらの用途で強力なツールとなるためには、JSONを効率的に扱うための組み込みサポートが不可欠でした。
このコミットは、Go言語の設計者の一人であるRuss Cox氏によって行われ、Goの標準ライブラリにJSONのパース、シリアライズ、デシリアライズ機能の基礎を築くことを目的としています。これにより、Goプログラムが外部システムとJSON形式でデータをやり取りする能力が提供され、Go言語の汎用性と実用性が向上しました。
前提知識の解説
JSON (JavaScript Object Notation)
JSONは、人間が読み書きしやすく、機械が解析しやすいデータ交換フォーマットです。JavaScriptのオブジェクトリテラルをベースにしており、キーと値のペアの集まり(オブジェクト)と、値の順序付きリスト(配列)という2つの基本的な構造でデータを表現します。Web APIや設定ファイルなどで広く利用されています。
Go言語の初期の設計思想
このコミットが行われた2008年時点のGo言語は、現在のGo言語とは異なる部分が多く存在します。特に、以下の点に注意が必要です。
export
キーワード: 当時のGo言語には、パッケージ外に公開する要素を示すためにexport
キーワードが存在しました。これは後のバージョンで削除され、識別子が大文字で始まることで公開されるという現在のルールに変わりました。array
パッケージ: 現在のGoには組み込みの[]type
スライスがありますが、この時期にはarray
パッケージ(container/array
)のような、より低レベルな配列操作を提供するパッケージが存在していた可能性があります。reflect
パッケージの初期段階: Goのreflect
パッケージは、実行時に型情報を検査・操作するための強力な機能を提供します。このコミットでは、JSONデータをGoの構造体(struct)にマッピングするためにreflect
パッケージが利用されていますが、そのAPIや機能は現在のものとは異なる可能性があります。Makefile
ベースのビルドシステム: 当時のGoプロジェクトは、Makefile
を使用してビルドプロセスを管理していました。これは、後のgo build
コマンドのような統合されたツールが登場する前の段階です。
データ構造のマーシャリングとアンマーシャリング
- マーシャリング (Marshaling): プログラム内のデータ構造(例: Goの構造体)を、外部に保存または送信できる形式(例: JSON文字列)に変換するプロセスです。
- アンマーシャリング (Unmarshaling): 外部形式のデータ(例: JSON文字列)を、プログラム内のデータ構造に変換するプロセスです。
このコミットでは、JSON文字列をGoの構造体にアンマーシャリングする機能が主に実装されています。
技術的詳細
このJSONライブラリは、主に以下の3つのGoファイルで構成されています。
-
generic.go
:- JSONの各データ型(文字列、数値、マップ、配列、真偽値、null)を表現するためのインターフェース
Json
と、具体的な型(String
,Number
,Array
,Bool
,Map
,Null
)を定義しています。 - これらの型は、JSONの階層構造をGoのインターフェースと構造体で表現するための基盤となります。
JsonToString
関数は、Json
インターフェースを実装する値をJSON文字列に変換します。Walk
関数は、JSONオブジェクトのパスを指定して要素にアクセスする機能を提供します。Equal
関数は、2つのJson
オブジェクトが等しいかどうかを比較します。JsonBuilder
は、JSONのパース時に動的にJson
オブジェクトを構築するためのヘルパー構造体です。
- JSONの各データ型(文字列、数値、マップ、配列、真偽値、null)を表現するためのインターフェース
-
parse.go
:- JSON文字列をトークンに分割するLexer(字句解析器)を実装しています。
Next()
メソッドが次のトークンを読み込み、その種類と値を設定します。 - JSONの文法に従ってトークンを解析し、
Builder
インターフェースを通じてデータ構造を構築するParser(構文解析器)を実装しています。 Unquote
関数は、JSON文字列リテラルからエスケープシーケンスを解除します。Quote
関数は、Goの文字列をJSON文字列リテラルにエスケープします。Parse
関数は、JSON文字列とBuilder
を受け取り、パースを実行します。
- JSON文字列をトークンに分割するLexer(字句解析器)を実装しています。
-
struct.go
:- Goの
reflect
パッケージを利用して、JSONデータをGoの構造体にアンマーシャリングする機能を提供します。 StructBuilder
はBuilder
インターフェースを実装し、パースされたJSONの値をGoの構造体のフィールドにマッピングします。Unmarshal
関数は、JSON文字列とGoのインターフェース(通常は構造体へのポインタ)を受け取り、JSONデータをGoの構造体にデシリアライズします。- このファイルは、Goの初期の
reflect
パッケージの利用方法を示しており、型アサーション(例:v.(reflect.FloatValue)
)が多用されているのが特徴です。これは、当時のreflect
パッケージのAPIが現在よりも低レベルであったことを示唆しています。
- Goの
ビルドシステムへの統合
src/lib/Makefile
: 新しいjson
ライブラリをビルドシステムに組み込むために、DIRS
変数にjson
を追加し、json.dirinstall
ターゲットの依存関係を定義しています。これにより、json
パッケージがGoの標準ライブラリの一部としてビルドされるようになります。src/lib/json/Makefile
:json
パッケージ自体のビルド方法を定義するMakefile
です。Goのソースファイル(.go
)をコンパイルし、アーカイブファイル(.a
)を作成する手順が含まれています。gobuild
というツールが自動生成したものであることがコメントから読み取れます。src/run.bash
: テスト実行スクリプトにlib/json
を追加し、JSONライブラリのテストが自動的に実行されるようにしています。
コアとなるコードの変更箇所
このコミットでは、主に以下のファイルが新規追加されています。
src/lib/json/Makefile
src/lib/json/generic.go
src/lib/json/generic_test.go
src/lib/json/parse.go
src/lib/json/struct.go
src/lib/json/struct_test.go
既存ファイルへの変更は以下の通りです。
src/lib/Makefile
:--- a/src/lib/Makefile +++ b/src/lib/Makefile @@ -12,6 +12,7 @@ DIRS=\ hash\ http\ io\ + json\ math\ net\ os\ @@ -94,6 +95,8 @@ fmt.dirinstall: io.dirinstall reflect.dirinstall strconv.dirinstall hash.dirinstall: os.dirinstall http.dirinstall: bufio.install io.dirinstall net.dirinstall os.dirinstall strings.install io.dirinstall: os.dirinstall syscall.dirinstall +json.dirinstall: container/array.dirinstall fmt.dirinstall io.dirinstall math.dirinstall \ +\tstrconv.dirinstall strings.install utf8.install net.dirinstall: fmt.dirinstall once.install os.dirinstall strconv.dirinstall os.dirinstall: syscall.dirinstall regexp.dirinstall: os.dirinstall
src/run.bash
:--- a/src/run.bash +++ b/src/run.bash @@ -26,6 +26,7 @@ maketest() { maketest \ lib/fmt\ lib/hash\ + lib/json\ lib/math\ lib/reflect\ lib/regexp\
コアとなるコードの解説
src/lib/json/generic.go
このファイルは、JSONの汎用的な表現を定義しています。
export type Json interface {
Kind() int;
String() string;
Number() float64;
Bool() bool;
Get(s string) Json; // For map access
Elem(i int) Json; // For array access
Len() int; // For array/map length
}
export const (
StringKind = iota;
NumberKind;
MapKind;
ArrayKind;
BoolKind;
NullKind;
)
Json
インターフェースは、JSONのあらゆる値を抽象化します。Kind()
メソッドでその型を識別し、String()
, Number()
, Bool()
などで具体的な値を取得します。Get()
はJSONオブジェクトのキーによるアクセス、Elem()
はJSON配列のインデックスによるアクセス、Len()
は配列やオブジェクトの長さを取得するために使われます。
JsonBuilder
は、パース中にJSON構造を構築するための重要なコンポーネントです。
type JsonBuilder struct {
ptr *Json; // Target for simple values
a *array.Array; // Target for array elements
i int; // Index for array elements
m *map[string] Json; // Target for map keys
k string; // Key for map elements
}
func (b *JsonBuilder) Put(j Json) {
switch {
case b.ptr != nil:
*b.ptr = j;
case b.a != nil:
b.a.Set(b.i, j);
case b.m != nil:
b.m[b.k] = j;
}
}
JsonBuilder
は、パースされたJSONの値をどこに格納するかを管理します。Put
メソッドは、現在のビルダが指す場所(ポインタ、配列の要素、マップのキー)にJSON値を設定します。Elem
やKey
メソッドは、ネストされた構造を構築するために新しいJsonBuilder
インスタンスを返します。
src/lib/json/parse.go
このファイルは、JSONの字句解析(Lexer)と構文解析(Parser)を担当します。
type Lexer struct {
s string; // Input string
i int; // Current position
kind int; // Token kind
token string; // Token value
}
func (t *Lexer) Next() {
// Skips whitespace and identifies the next token (number, string, keyword, punctuation)
// and updates t.kind and t.token.
}
Lexer
は、入力JSON文字列を走査し、JSONの構成要素(数値、文字列、true
, false
, null
、区切り文字など)を識別します。Next()
メソッドが呼び出されるたびに、次の有効なトークンを読み込み、その種類と値をLexer
構造体のフィールドに格納します。
export type Builder interface {
// Set value methods
Int64(i int64);
Uint64(i uint64);
Float64(f float64);
String(s string);
Bool(b bool);
Null();
Array();
Map();
// Create sub-Builders
Elem(i int) Builder;
Key(s string) Builder;
}
func ParseValue(lex *Lexer, build Builder) bool {
// Parses a JSON value based on the current token from the lexer,
// and uses the Builder interface to construct the corresponding Go value.
}
export func Parse(s string, build Builder) (ok bool, errindx int, errtok string) {
// Initializes lexer and calls ParseValue to start parsing.
}
Builder
インターフェースは、パースされたJSONの値をどのように構築するかを抽象化します。Int64
, String
, Array
, Map
などのメソッドは、対応するJSONの値をGoのデータ構造に変換する役割を担います。ParseValue
関数は、Lexer
からトークンを読み取り、Builder
インターフェースのメソッドを呼び出すことで、JSONの階層構造を再構築します。Parse
関数は、このプロセスを開始するエントリポイントです。
src/lib/json/struct.go
このファイルは、Goのreflect
パッケージを使用して、JSONデータをGoの構造体にアンマーシャリングする機能を提供します。
type StructBuilder struct {
val reflect.Value
}
func (b *StructBuilder) Int64(i int64) {
// Sets the underlying reflect.Value to the integer value, handling type conversions.
}
// Similar methods for Uint64, Float64, Null, String, Bool
func (b *StructBuilder) Array() {
// Initializes an array if the target reflect.Value is a pointer to an array.
}
func (b *StructBuilder) Elem(i int) Builder {
// Returns a new StructBuilder for the i-th element of an array,
// dynamically growing the array if necessary.
}
func (b *StructBuilder) Map() {
// Initializes a map if the target reflect.Value is a pointer to a map.
}
func (b *StructBuilder) Key(k string) Builder {
// Returns a new StructBuilder for the field named 'k' in a struct,
// using reflection to find the field.
}
export func Unmarshal(s string, val interface{}) (ok bool, errtok string) {
// Creates a StructBuilder from the provided interface{} (usually a pointer to a struct),
// and then calls Parse to populate the struct.
}
StructBuilder
は、Builder
インターフェースを実装し、reflect.Value
を内部に保持します。これにより、パースされたJSONの値を、実行時に指定されたGoの構造体のフィールドに直接設定することができます。Elem
メソッドは配列の要素に、Key
メソッドは構造体のフィールドにアクセスするための新しいStructBuilder
を返します。Unmarshal
関数は、JSON文字列をGoの構造体にデシリアライズするための主要なエントリポイントです。
関連リンク
- Go言語の公式ウェブサイト: https://go.dev/
- Go言語の初期の歴史に関する情報(Goのブログなど)
参考にした情報源リンク
- JSONの公式ウェブサイト: https://www.json.org/json-ja.html
- Go言語の
encoding/json
パッケージのドキュメント(現在のバージョン): https://pkg.go.dev/encoding/json- このコミットのコードとは異なりますが、現在のGoのJSON処理の標準的な方法を理解する上で参考になります。
- Go言語の
reflect
パッケージのドキュメント(現在のバージョン): https://pkg.go.dev/reflect- このコミットのコードとは異なりますが、Goのリフレクションの概念を理解する上で参考になります。
- Go言語の初期のコミット履歴(GitHubリポジトリ): https://github.com/golang/go/commits/master
- このコミットがGoの歴史の中でどのような位置づけにあるかを理解する上で役立ちます。