[インデックス 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.Marshaler
とjson.Unmarshaler
インターフェースを実装する必要がありました。これらのインターフェースは、JSONのバイト列を直接扱うことを要求します。
しかし、Goの標準ライブラリには、encoding
パッケージで定義されているencoding.TextMarshaler
とencoding.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.Marshaler
とjson.Unmarshaler
に依存していました。このコミットは、encoding.TextMarshaler
とencoding.TextUnmarshaler
のサポートを追加することで、このメカニズムを拡張します。
JSONの文字列値
JSONでは、文字列は二重引用符で囲まれたUnicode文字のシーケンスです。encoding.TextMarshaler
やencoding.TextUnmarshaler
が扱うテキストデータは、JSONの文脈ではこの文字列値として扱われることが期待されます。つまり、MarshalText
が返すバイトスライスはJSON文字列としてエンコードされ、UnmarshalText
にはJSON文字列がデコードされた結果のバイトスライスが渡されます。
技術的詳細
このコミットの主要な変更点は、encoding/json
パッケージのエンコーダとデコーダが、json.Marshaler
/json.Unmarshaler
インターフェースのチェックに加えて、encoding.TextMarshaler
/encoding.TextUnmarshaler
インターフェースもチェックするようになったことです。
エンコーディング (encode.go
)
-
newTypeEncoder
関数の変更:- この関数は、特定の
reflect.Type
に対応するエンコーダ関数を決定します。 - 変更前は、
json.Marshaler
インターフェースの実装のみをチェックしていました。 - 変更後は、まず
json.Marshaler
のチェックを行い、その後にencoding.TextMarshaler
のチェックを行うようになりました。 - これにより、型が
encoding.TextMarshaler
を実装している場合、新しいtextMarshalerEncoder
またはaddrTextMarshalerEncoder
が使用されるようになります。
- この関数は、特定の
-
新しいエンコーダ関数の追加:
textMarshalerEncoder
:encoding.TextMarshaler
を値レシーバで実装している型を処理します。addrTextMarshalerEncoder
:encoding.TextMarshaler
をポインタレシーバで実装している型を処理します。- これらの関数は、
MarshalText()
メソッドを呼び出し、その結果のバイトスライスをJSON文字列としてエンコードします。エラーが発生した場合は、MarshalerError
を返します。
-
stringBytes
関数の追加:string
関数(Goの文字列をJSON文字列としてエンコードする)とほぼ同じロジックを持つstringBytes
関数が追加されました。stringBytes
は[]byte
を受け取り、それをJSON文字列としてエンコードします。これはencoding.TextMarshaler
が返す[]byte
を処理するために必要です。- コメントに「NOTE: keep in sync with string above.」とあり、両関数のロジックが同期されていることが強調されています。これは、文字列のエスケープルールがJSONの仕様に厳密に従う必要があるためです。
デコーディング (decode.go
)
-
indirect
関数の変更:- この関数は、
reflect.Value
が指す実際の値を取得し、その値がjson.Unmarshaler
またはencoding.TextUnmarshaler
を実装しているかどうかをチェックします。 - 変更前は、
json.Unmarshaler
のみを返していました。 - 変更後は、
json.Unmarshaler
とencoding.TextUnmarshaler
の両方を返すようになりました。これにより、デコーダはどちらのインターフェースが実装されているかに応じて適切な処理を選択できます。
- この関数は、
-
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
パッケージは、複数のマーシャリング/アンマーシャリングインターフェースが実装されている場合の優先順位を定めています。このコミット後も、その優先順位は維持されます。
json.Marshaler
/json.Unmarshaler
: これらが実装されている場合、JSONエンコーダ/デコーダはこれらを最優先します。これは、型がJSON固有の表現を完全に制御したい場合に最も強力なメカニズムだからです。encoding.TextMarshaler
/encoding.TextUnmarshaler
:json.Marshaler
/json.Unmarshaler
が実装されていない場合、次にこれらのインターフェースがチェックされます。これらのインターフェースは、型が自身をテキストとして表現する方法を定義し、JSONの文字列値との間で変換されます。- デフォルトの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
) をチェックするようになりました。 - もし
ut
がnil
でなければ、その型がencoding.TextUnmarshaler
を実装していると判断し、JSONの入力データ(文字列として期待される)をUnmarshalText
メソッドに渡してデコードを試みます。 array
とobject
の場合、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
が使用されます。
-
新しいエンコーダ関数
textMarshalerEncoder
とaddrTextMarshalerEncoder
の追加:- これらの関数は、
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.TextMarshaler
とencoding.TextUnmarshaler
を実装するカスタム型を、JSONの文字列値として透過的に扱うことができるようになりました。これにより、開発者はJSONのカスタムマーシャリング/アンマーシャリングの選択肢が増え、より柔軟なデータ変換が可能になります。
関連リンク
- Go言語
encoding
パッケージのドキュメント: https://pkg.go.dev/encoding - Go言語
encoding/json
パッケージのドキュメント: https://pkg.go.dev/encoding/json - Go言語のインターフェースに関する公式ドキュメント: https://go.dev/tour/methods/10
参考にした情報源リンク
- 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.go
のstringBytes
関数内のコメントで参照されています)