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

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

このコミットは、Go言語の標準ライブラリである encoding/json パッケージに、encoding.TextMarshaler および encoding.TextUnmarshaler インターフェースのサポートを追加するものです。これにより、JSONエンコーディングおよびデコーディングの際に、これらのテキストベースのマーシャリング/アンマーシャリングインターフェースを実装したカスタム型をより柔軟に扱えるようになります。

コミット

commit 7e886740d1c4b62bf7aea2a71048be432aeff945
Author: Russ Cox <rsc@golang.org>
Date:   Wed Aug 14 14:56:07 2013 -0400

    encoding/json: support encoding.TextMarshaler, encoding.TextUnmarshaler
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/12703043

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

https://github.com/golang/go/commit/7e886740d1c4b62bf7aea2a71048be432aeff945

元コミット内容

encoding/json: support encoding.TextMarshaler, encoding.TextUnmarshaler

変更の背景

Goのencoding/jsonパッケージは、Goのデータ構造とJSONデータの間で変換を行うための標準的な方法を提供します。これまで、カスタム型がJSONとの間で自身をマーシャリング/アンマーシャリングする方法を制御するためには、json.Marshalerjson.Unmarshalerインターフェースを実装する必要がありました。これらのインターフェースは、JSONのバイト列を直接扱うことを要求します。

しかし、Goの標準ライブラリには、encodingパッケージで定義されているencoding.TextMarshalerencoding.TextUnmarshalerという、より汎用的なテキスト表現への変換を扱うインターフェースも存在します。これらは、例えばfmtパッケージのStringerインターフェースのように、型が自身をテキストとして表現する方法を定義するのに使われます。

このコミットの背景には、JSONデータがしばしば文字列として表現される(特にプリミティブな値や、カスタムフォーマットのデータがJSON文字列として埋め込まれる場合)という事実があります。json.Marshaler/json.Unmarshalerは強力ですが、テキスト表現への変換というより一般的なニーズに対しては、encoding.TextMarshaler/encoding.TextUnmarshalerの方が自然な選択肢となる場合があります。

この変更により、encoding/jsonパッケージがこれらのテキストインターフェースを認識し、JSONの文字列値との間で自動的に変換を行うことで、開発者はより簡潔で再利用可能なコードを書けるようになります。例えば、time.Time型がJSONで文字列として扱われるように、カスタム型も同様に文字列として扱えるようになります。これにより、JSONの柔軟性が向上し、より多くのGoの型が特別なjson.Marshaler/json.Unmarshalerの実装なしにJSONと相互運用できるようになります。

前提知識の解説

Go言語のインターフェース

Go言語のインターフェースは、メソッドのシグネチャの集まりを定義する型です。ある型がインターフェースのすべてのメソッドを実装している場合、その型はそのインターフェースを満たしていると見なされます。Goのインターフェースは暗黙的に満たされるため、implementsキーワードのような明示的な宣言は不要です。

encodingパッケージ

Goの標準ライブラリのencodingパッケージは、データとテキスト表現の間で変換を行うための共通インターフェースを提供します。このコミットで関連するのは以下の二つのインターフェースです。

  • encoding.TextMarshaler:

    type TextMarshaler interface {
        MarshalText() (text []byte, err error)
    }
    

    このインターフェースを実装する型は、自身をテキスト形式のバイトスライスに変換する方法を定義します。例えば、time.Time型はこのインターフェースを実装しており、日付と時刻をRFC3339形式の文字列としてマーシャリングします。

  • encoding.TextUnmarshaler:

    type TextUnmarshaler interface {
        UnmarshalText(text []byte) error
    }
    

    このインターフェースを実装する型は、テキスト形式のバイトスライスから自身を再構築する方法を定義します。MarshalTextの逆の操作を行います。

encoding/jsonパッケージ

Goの標準ライブラリのencoding/jsonパッケージは、Goのデータ構造とJSONデータの間で変換を行う機能を提供します。

  • json.Marshaler:

    type Marshaler interface {
        MarshalJSON() ([]byte, error)
    }
    

    このインターフェースを実装する型は、自身をJSON形式のバイトスライスに変換する方法を定義します。これにより、型のデフォルトのJSONエンコーディング動作をオーバーライドできます。

  • json.Unmarshaler:

    type Unmarshaler interface {
        UnmarshalJSON([]byte) error
    }
    

    このインターフェースを実装する型は、JSON形式のバイトスライスから自身を再構築する方法を定義します。これにより、型のデフォルトのJSONデコーディング動作をオーバーライドできます。

これまでのencoding/jsonパッケージは、カスタムマーシャリング/アンマーシャリングのために主にjson.Marshalerjson.Unmarshalerに依存していました。このコミットは、encoding.TextMarshalerencoding.TextUnmarshalerのサポートを追加することで、このメカニズムを拡張します。

JSONの文字列値

JSONでは、文字列は二重引用符で囲まれたUnicode文字のシーケンスです。encoding.TextMarshalerencoding.TextUnmarshalerが扱うテキストデータは、JSONの文脈ではこの文字列値として扱われることが期待されます。つまり、MarshalTextが返すバイトスライスはJSON文字列としてエンコードされ、UnmarshalTextにはJSON文字列がデコードされた結果のバイトスライスが渡されます。

技術的詳細

このコミットの主要な変更点は、encoding/jsonパッケージのエンコーダとデコーダが、json.Marshaler/json.Unmarshalerインターフェースのチェックに加えて、encoding.TextMarshaler/encoding.TextUnmarshalerインターフェースもチェックするようになったことです。

エンコーディング (encode.go)

  1. newTypeEncoder関数の変更:

    • この関数は、特定のreflect.Typeに対応するエンコーダ関数を決定します。
    • 変更前は、json.Marshalerインターフェースの実装のみをチェックしていました。
    • 変更後は、まずjson.Marshalerのチェックを行い、その後にencoding.TextMarshalerのチェックを行うようになりました。
    • これにより、型がencoding.TextMarshalerを実装している場合、新しいtextMarshalerEncoderまたはaddrTextMarshalerEncoderが使用されるようになります。
  2. 新しいエンコーダ関数の追加:

    • textMarshalerEncoder: encoding.TextMarshalerを値レシーバで実装している型を処理します。
    • addrTextMarshalerEncoder: encoding.TextMarshalerをポインタレシーバで実装している型を処理します。
    • これらの関数は、MarshalText()メソッドを呼び出し、その結果のバイトスライスをJSON文字列としてエンコードします。エラーが発生した場合は、MarshalerErrorを返します。
  3. stringBytes関数の追加:

    • string関数(Goの文字列をJSON文字列としてエンコードする)とほぼ同じロジックを持つstringBytes関数が追加されました。
    • stringBytes[]byteを受け取り、それをJSON文字列としてエンコードします。これはencoding.TextMarshalerが返す[]byteを処理するために必要です。
    • コメントに「NOTE: keep in sync with string above.」とあり、両関数のロジックが同期されていることが強調されています。これは、文字列のエスケープルールがJSONの仕様に厳密に従う必要があるためです。

デコーディング (decode.go)

  1. indirect関数の変更:

    • この関数は、reflect.Valueが指す実際の値を取得し、その値がjson.Unmarshalerまたはencoding.TextUnmarshalerを実装しているかどうかをチェックします。
    • 変更前は、json.Unmarshalerのみを返していました。
    • 変更後は、json.Unmarshalerencoding.TextUnmarshalerの両方を返すようになりました。これにより、デコーダはどちらのインターフェースが実装されているかに応じて適切な処理を選択できます。
  2. array, object, literalStore関数の変更:

    • これらのデコード処理を行う関数は、indirect関数から返されるencoding.TextUnmarshalerの有無をチェックするようになりました。
    • encoding.TextUnmarshalerが実装されている場合、JSONの配列やオブジェクト、リテラル(文字列、数値、真偽値、null)がその型にデコードされる際に、UnmarshalTextメソッドが呼び出されます。
    • 特にliteralStore関数では、JSONの文字列値("で始まる)がUnmarshalTextに渡されるように処理が追加されています。もしJSONの入力が文字列ではないのにencoding.TextUnmarshalerが実装されている場合(例: JSONの数値がencoding.TextUnmarshalerを実装した型にデコードされようとする場合)、UnmarshalTypeErrorが報告されます。これは、encoding.TextUnmarshalerがテキスト表現を期待するためです。
    • ",string"タグのサポートも改善され、encoding.TextUnmarshalerが文字列として扱われるべきフィールドに適用された場合の挙動がより堅牢になりました。

優先順位

encoding/jsonパッケージは、複数のマーシャリング/アンマーシャリングインターフェースが実装されている場合の優先順位を定めています。このコミット後も、その優先順位は維持されます。

  1. json.Marshaler / json.Unmarshaler: これらが実装されている場合、JSONエンコーダ/デコーダはこれらを最優先します。これは、型がJSON固有の表現を完全に制御したい場合に最も強力なメカニズムだからです。
  2. encoding.TextMarshaler / encoding.TextUnmarshaler: json.Marshaler / json.Unmarshalerが実装されていない場合、次にこれらのインターフェースがチェックされます。これらのインターフェースは、型が自身をテキストとして表現する方法を定義し、JSONの文字列値との間で変換されます。
  3. デフォルトのJSONエンコーディング/デコーディング: 上記のインターフェースがどれも実装されていない場合、encoding/jsonパッケージはGoの型(構造体、スライス、マップ、プリミティブ型など)のデフォルトのJSONエンコーディング/デコーディングルールに従います。

この優先順位により、開発者は必要に応じてJSON固有の動作を細かく制御でき、より一般的なテキスト表現が必要な場合にはencoding.TextMarshalerなどを利用できる柔軟性が提供されます。

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

src/pkg/encoding/json/decode.go

--- a/src/pkg/encoding/json/decode.go
+++ b/src/pkg/encoding/json/decode.go
@@ -293,7 +294,7 @@ func (d *decodeState) value(v reflect.Value) {
 // until it gets to a non-pointer.
 // if it encounters an Unmarshaler, indirect stops and returns that.
 // if decodingNull is true, indirect stops at the last pointer so it can be set to nil.
-func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, reflect.Value) {
+func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, encoding.TextUnmarshaler, reflect.Value) {
 	// If v is a named type and is addressable,
 	// start with its address, so that if the type has pointer methods,
 	// we find them.
@@ -322,28 +323,38 @@ func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler,
 		v.Set(reflect.New(v.Type().Elem()))
 	}
 	if v.Type().NumMethod() > 0 {
-		if unmarshaler, ok := v.Interface().(Unmarshaler); ok {
-			return unmarshaler, reflect.Value{}
+		if u, ok := v.Interface().(Unmarshaler); ok {
+			return u, nil, reflect.Value{}
+		}
+		if u, ok := v.Interface().(encoding.TextUnmarshaler); ok {
+			return nil, u, reflect.Value{}
 		}
 	}
 	v = v.Elem()
-	return nil, v
+	return nil, nil, v
 }
 
 // array consumes an array from d.data[d.off-1:], decoding into the value v.
 // the first byte of the array ('[') has been read already.
 func (d *decodeState) array(v reflect.Value) {
 	// Check for unmarshaler.
-	unmarshaler, pv := d.indirect(v, false)
-	if unmarshaler != nil {
+	u, ut, pv := d.indirect(v, false)
+	if u != nil {
 		d.off--
-		err := unmarshaler.UnmarshalJSON(d.next())
+		err := u.UnmarshalJSON(d.next())
 		if err != nil {
 			d.error(err)
 		}
 		return
 	}
+	if ut != nil {
+		d.saveError(&UnmarshalTypeError{"array", v.Type()})
+		d.off--
+		d.next()
+		return
+	}
+
 	v = pv
 
 	// Check type of target.
@@ -434,15 +455,21 @@ func (d *decodeState) array(v reflect.Value) {
 // the first byte of the object ('{') has been read already.
 func (d *decodeState) object(v reflect.Value) {
 	// Check for unmarshaler.
-	unmarshaler, pv := d.indirect(v, false)
-	if unmarshaler != nil {
+	u, ut, pv := d.indirect(v, false)
+	if u != nil {
 		d.off--
-		err := unmarshaler.UnmarshalJSON(d.next())
+		err := u.UnmarshalJSON(d.next())
 		if err != nil {
 			d.error(err)
 		}
 		return
 	}
+	if ut != nil {
+		d.saveError(&UnmarshalTypeError{"object", v.Type()})
+		d.off--
+		d.next() // skip over { } in input
+		return
+	}
 	v = pv
 
 	// Decoding into nil interface?  Switch to non-reflect code.
@@ -611,14 +648,37 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
 		return
 	}
 	wantptr := item[0] == 'n' // null
-	unmarshaler, pv := d.indirect(v, wantptr)
-	if unmarshaler != nil {
-		err := unmarshaler.UnmarshalJSON(item)
+	u, ut, pv := d.indirect(v, wantptr)
+	if u != nil {
+		err := u.UnmarshalJSON(item)
 		if err != nil {
 			d.error(err)
 		}
 		return
 	}
+	if ut != nil {
+		if item[0] != '"' {
+			if fromQuoted {
+				d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
+			} else {
+				d.saveError(&UnmarshalTypeError{"string", v.Type()})
+			}
+		}
+		s, ok := unquoteBytes(item)
+		if !ok {
+			if fromQuoted {
+				d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
+			} else {
+				d.error(errPhase)
+			}
+		}
+		err := ut.UnmarshalText(s)
+		if err != nil {
+			d.error(err)
+		}
+		return
+	}
+
 	v = pv
 
 	switch c := item[0]; c {

src/pkg/encoding/json/encode.go

--- a/src/pkg/encoding/json/encode.go
+++ b/src/pkg/encoding/json/encode.go
@@ -361,17 +362,29 @@ func newTypeEncoder(t reflect.Type, vx reflect.Value) encoderFunc {
 	if !vx.IsValid() {
 		vx = reflect.New(t).Elem()
 	}
+
 	_, ok := vx.Interface().(Marshaler)
 	if ok {
-		return valueIsMarshallerEncoder
+		return marshalerEncoder
 	}
-	// T doesn't match the interface. Check against *T too.
 	if vx.Kind() != reflect.Ptr && vx.CanAddr() {
 		_, ok = vx.Addr().Interface().(Marshaler)
 		if ok {
-			return valueAddrIsMarshallerEncoder
+			return addrMarshalerEncoder
 		}
 	}
+
+	_, ok = vx.Interface().(encoding.TextMarshaler)
+	if ok {
+		return textMarshalerEncoder
+	}
+	if vx.Kind() != reflect.Ptr && vx.CanAddr() {
+		_, ok = vx.Addr().Interface().(encoding.TextMarshaler)
+		if ok {
+			return addrTextMarshalerEncoder
+		}
+	}
+
 	switch vx.Kind() {
 	case reflect.Bool:
 		return boolEncoder
@@ -406,7 +419,7 @@ func invalidValueEncoder(e *encodeState, v reflect.Value, quoted bool) {
 	e.WriteString("null")
 }
 
-func valueIsMarshallerEncoder(e *encodeState, v reflect.Value, quoted bool) {
+func marshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
 	if v.Kind() == reflect.Ptr && v.IsNil() {
 		e.WriteString("null")
 		return
@@ -422,9 +435,9 @@ func valueIsMarshallerEncoder(e *encodeState, v reflect.Value, quoted bool) {
 	}
 }
 
-func valueAddrIsMarshallerEncoder(e *encodeState, v reflect.Value, quoted bool) {
+func addrMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
 	va := v.Addr()
-	if va.Kind() == reflect.Ptr && va.IsNil() {
+	if va.IsNil() {
 		e.WriteString("null")
 		return
 	}
@@ -439,6 +452,37 @@ func valueAddrIsMarshallerEncoder(e *encodeState, v reflect.Value, quoted bool)
 	}
 }
 
+func textMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
+	if v.Kind() == reflect.Ptr && v.IsNil() {
+		e.WriteString("null")
+		return
+	}
+	m := v.Interface().(encoding.TextMarshaler)
+	b, err := m.MarshalText()
+	if err == nil {
+		_, err = e.stringBytes(b)
+	}
+	if err != nil {
+		e.error(&MarshalerError{v.Type(), err})
+	}
+}
+
+func addrTextMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
+	va := v.Addr()
+	if va.IsNil() {
+		e.WriteString("null")
+		return
+	}
+	m := va.Interface().(encoding.TextMarshaler)
+	b, err := m.MarshalText()
+	if err == nil {
+		_, err = e.stringBytes(b)
+	}
+	if err != nil {
+		e.error(&MarshalerError{v.Type(), err})
+	}
+}
+
 func boolEncoder(e *encodeState, v reflect.Value, quoted bool) {
 	if quoted {
 		e.WriteByte('"')
@@ -728,6 +772,79 @@ func (sv stringValues) Swap(i, j int)      { sv[i], sv[j] = sv[j], sv[i] }\n func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) }\n func (sv stringValues) get(i int) string   { return sv[i].String() }\n \n+// NOTE: keep in sync with stringBytes below.\n func (e *encodeState) string(s string) (int, error) {\n 	len0 := e.Len()\n 	e.WriteByte('"')\n@@ -800,6 +845,79 @@ func (e *encodeState) string(s string) (int, error) {\n 	return e.Len() - len0, nil\n }\n \n+// NOTE: keep in sync with string above.\n+func (e *encodeState) stringBytes(s []byte) (int, error) {\n+\tlen0 := e.Len()\n+\te.WriteByte('"')\n+\tstart := 0\n+\tfor i := 0; i < len(s); {\n+\t\tif b := s[i]; b < utf8.RuneSelf {\n+\t\t\tif 0x20 <= b && b != '\\\\' && b != '"' && b != '<' && b != '>' && b != '&' {\n+\t\t\t\ti++\n+\t\t\t\tcontinue\n+\t\t\t}\n+\t\t\tif start < i {\n+\t\t\t\te.Write(s[start:i])\n+\t\t\t}\n+\t\t\tswitch b {\n+\t\t\tcase '\\\\', '"':\n+\t\t\t\te.WriteByte('\\\\')\n+\t\t\t\te.WriteByte(b)\n+\t\t\tcase '\\n':\n+\t\t\t\te.WriteByte('\\\\')\n+\t\t\t\te.WriteByte('n')\n+\t\t\tcase '\\r':\n+\t\t\t\te.WriteByte('\\\\')\n+\t\t\t\te.WriteByte('r')\n+\t\t\tdefault:\n+\t\t\t\t// This encodes bytes < 0x20 except for \\n and \\r,\n+\t\t\t\t// as well as < and >. The latter are escaped because they\n+\t\t\t\t// can lead to security holes when user-controlled strings\n+\t\t\t\t// are rendered into JSON and served to some browsers.\n+\t\t\t\te.WriteString(`\\u00`)\n+\t\t\t\te.WriteByte(hex[b>>4])\n+\t\t\t\te.WriteByte(hex[b&0xF])\n+\t\t\t}\n+\t\t\ti++\n+\t\t\tstart = i\n+\t\t\tcontinue\n+\t\t}\n+\t\tc, size := utf8.DecodeRune(s[i:])\n+\t\tif c == utf8.RuneError && size == 1 {\n+\t\t\tif start < i {\n+\t\t\t\te.Write(s[start:i])\n+\t\t\t}\n+\t\t\te.WriteString(`\\ufffd`)\n+\t\t\ti += size\n+\t\t\tstart = i\n+\t\t\tcontinue\n+\t\t}\n+\t\t// U+2028 is LINE SEPARATOR.\n+\t\t// U+2029 is PARAGRAPH SEPARATOR.\n+\t\t// They are both technically valid characters in JSON strings,\n+\t\t// but don't work in JSONP, which has to be evaluated as JavaScript,\n+\t\t// and can lead to security holes there. It is valid JSON to\n+\t\t// escape them, so we do so unconditionally.\n+\t\t// See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.\n+\t\tif c == '\\u2028' || c == '\\u2029' {\n+\t\t\tif start < i {\n+\t\t\t\te.Write(s[start:i])\n+\t\t\t}\n+\t\t\te.WriteString(`\\u202`)\n+\t\t\te.WriteByte(hex[c&0xF])\n+\t\t\ti += size\n+\t\t\tstart = i\n+\t\t\tcontinue\n+\t\t}\n+\t\ti += size\n+\t}\n+\tif start < len(s) {\n+\t\te.Write(s[start:])\n+\t}\n+\te.WriteByte('"')\n+\treturn e.Len() - len0, nil\n+}\n+\n // A field represents a single field found in a struct.\n type field struct {\n \tname      string

コアとなるコードの解説

decode.go の変更点

  • indirect関数のシグネチャ変更:

    • 変更前: func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, reflect.Value)
    • 変更後: func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, encoding.TextUnmarshaler, reflect.Value)
    • この変更により、indirect関数はjson.Unmarshalerだけでなく、encoding.TextUnmarshalerも返すようになりました。これにより、呼び出し元はどちらのインターフェースが実装されているかに応じて適切なデコードロジックを選択できます。
  • indirect関数内のインターフェースチェックロジック:

    • v.Interface().(Unmarshaler)のチェックに加えて、v.Interface().(encoding.TextUnmarshaler)のチェックが追加されました。
    • json.Unmarshalerが優先され、もし存在すればそれが返されます。そうでなければencoding.TextUnmarshalerがチェックされます。
  • array, object, literalStore関数でのencoding.TextUnmarshalerの利用:

    • これらの関数は、indirectから返されたencoding.TextUnmarshaler (ut) をチェックするようになりました。
    • もしutnilでなければ、その型がencoding.TextUnmarshalerを実装していると判断し、JSONの入力データ(文字列として期待される)をUnmarshalTextメソッドに渡してデコードを試みます。
    • arrayobjectの場合、JSONの入力が配列やオブジェクトであるにもかかわらずencoding.TextUnmarshalerが実装されている場合は、UnmarshalTypeErrorを発生させます。これは、encoding.TextUnmarshalerがJSONの文字列値にのみ適用されるべきであるためです。
    • literalStoreでは、JSONの入力が文字列でない場合にUnmarshalTypeErrorを発生させるロジックが追加されました。また、unquoteBytesを使ってJSON文字列から引用符を取り除き、その結果をUnmarshalTextに渡しています。

encode.go の変更点

  • newTypeEncoder関数内のインターフェースチェックロジック:

    • json.Marshalerのチェックに加えて、encoding.TextMarshalerのチェックが追加されました。
    • json.Marshalerが優先され、もし存在すればmarshalerEncoderまたはaddrMarshalerEncoderが使用されます。そうでなければencoding.TextMarshalerがチェックされ、textMarshalerEncoderまたはaddrTextMarshalerEncoderが使用されます。
  • 新しいエンコーダ関数 textMarshalerEncoderaddrTextMarshalerEncoder の追加:

    • これらの関数は、encoding.TextMarshalerインターフェースを実装する型をJSONにエンコードするために使用されます。
    • MarshalText()メソッドを呼び出してテキスト形式のバイトスライスを取得し、そのバイトスライスをe.stringBytes()関数を使ってJSON文字列としてエンコードします。
    • e.stringBytes()は、Goの文字列をJSON文字列としてエンコードする既存のe.string()関数と同様のロジックを持ちますが、入力が[]byteである点が異なります。
  • stringBytes関数の追加:

    • string関数(string型をJSON文字列にエンコード)とほぼ同一のロジックを持つstringBytes関数が追加されました。
    • この関数は[]byteを受け取り、それをJSON文字列として適切にエスケープしてエンコードします。
    • encoding.TextMarshalerが返す[]byteをJSON文字列として出力するために不可欠な関数です。

これらの変更により、encoding/jsonパッケージは、encoding.TextMarshalerencoding.TextUnmarshalerを実装するカスタム型を、JSONの文字列値として透過的に扱うことができるようになりました。これにより、開発者はJSONのカスタムマーシャリング/アンマーシャリングの選択肢が増え、より柔軟なデータ変換が可能になります。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (上記に記載)
  • コミットメッセージと変更されたソースコード
  • Go言語のインターフェースの動作に関する一般的な知識
  • JSONの仕様に関する一般的な知識
  • time.Time型がencoding.TextMarshalerを実装していることに関する知識 (例: https://pkg.go.dev/time#Time.MarshalText)
  • Goのencoding/jsonパッケージの内部動作に関する一般的な知識 (例: reflectパッケージの利用)
  • Goのコードレビューシステム (Gerrit) のCL (Change-list) リンク: https://golang.org/cl/12703043 (これはコミットメッセージに記載されているものです)
  • JSONPにおけるセキュリティホールに関する議論 (コミット内のコメントで参照されているリンク): http://timelessrepo.com/json-isnt-a-javascript-subset (これはencode.gostringBytes関数内のコメントで参照されています)