を返します.GOにおけるJSON処理
30727 ワード
あなたが私のようで、バックエンド開発を学びたいならば、あなたはおそらく1点でJSON処理に遭遇しました.JSONはフロントエンドとバックエンドの間でデータを転送するための非常に一般的な形式です.それは近代的なWeb開発のような重要な機能ですので、GO
問題は、一つのやり方しかないということです.あなたが過去にいくつかのチュートリアルを見たならば、あなたはJSONを扱うために異なる機能を使用している人々に気がつきました.一部の人々の使用
JSONを読み書きするには二つの方法があります.このコードスニペットは、2つの方法を使用する方法を理解するのに役立ちます.最初に
イン
では、見てみましょう
これらの関数の使い方を知っているので、フードの下で2つのアプローチがどのように異なるかに飛び込むことができます.
の実装を見てみましょう.
また、どのように見てみましょう
encoding ()およびdecode ()のコードを示します:
良い質問!私は2つのアプローチのパフォーマンスの違いを見て好奇心旺盛だったので、私はテストを書いて、それらをベンチマーク.任意の印刷は、テストのために無効になっていることに注意してください.
テストのために設計されて
以下にテストコードを示します.
ここでは、テストのために使用されるアーキテクチャです.
JSONファイルサイズ
ループの実行
反復回数( ns/op )の時間
操作ごとに割り当てられたバイト数( B/OP )
操作あたりの割り当ての数
7
12819年
96463
56520年
582年
13
7323
155131
112736
1036年
31
3748年
315787
238736
1439年
175
679年
1647401
1300978
8992
1252年
94
12934180
10677126
84603年
25618
4
276607700
237844490
1750110
JSONファイルサイズ
ループの実行
反復回数( ns/op )の時間
操作ごとに割り当てられたバイト数( B/OP )
操作あたりの割り当ての数
7
13702年
88839
35432
580
13
7882
149191年
79312
1032年
31
4260
280336
149424
1433年
175
760
1583824
965830
8983年
1252年
94
12613618
7491156
84588
25618
4
261644025
166432316
1750103年
ここでいくつかのパターンを見ることができます. 他のすべては、それほど異なりません. 大きなJSONファイルを扱っているとき、メモリ使用量は問題になっているようですが、サーバーが巨大なJSONデータを受け取ることはunlinkです.私は最後の例でそれを押していました.
パフォーマンスの違いはかなり小さいので、どのアプローチを取るべきかを決めるときには、パフォーマンスはmakeまたはbreak factorであるべきではないと思います.考慮するより合理的な方法は、あなたがどのようなデータ形式を使っているかを見ることです.例えば、このスニペットを見てください.
テイクアウトポイントは、問題になるまでパフォーマンスを心配する必要はありません.代わりに、あなたは、与えられた時間で使用する最も簡単なソリューションを選ぶ必要があります.バイトスライスがあれば使用する
読んでくれてありがとう!これは私にとって興味深い話題であり、いくつかの実験をしたかった.コメントの下で私を知っている場合は、これらのタイプのポストのように!このポストを読むことができますMedium and my personal site 同様に.
encoding/json
パッケージ.問題は、一つのやり方しかないということです.あなたが過去にいくつかのチュートリアルを見たならば、あなたはJSONを扱うために異なる機能を使用している人々に気がつきました.一部の人々の使用
Marshal
and Unmarshal
, 他使用中Encode
and Decode
. 何を使うべきですか.どちらが良いですか.このブログ記事では、2つのアプローチの違いを説明しようとします.楽しむ!しかし、まず、2つのアプローチをお見せしましょう。
JSONを読み書きするには二つの方法があります.このコードスニペットは、2つの方法を使用する方法を理解するのに役立ちます.最初に
Marshal
and Unmarshal
:func PrettyPrint(v interface{}) (err error) {
b, err := json.MarshalIndent(v, "", "\t")
if err == nil {
fmt.Println(string(b))
}
return err
}
func TryMarshal() error {
data := map[string]interface{}{
"1": "one",
"2": "two",
"3": "three",
}
result, err := json.Marshal(&data)
if err != nil {
return err
}
err = PrettyPrint(result)
if err != nil {
return err
}
return nil
}
func TryUnmarshal() error {
myFile, err := os.Open("test.csv")
if err != nil {
return err
}
defer myFile.Close()
data, err := io.ReadAll(myFile)
if err != nil {
return err
}
var result map[string]interface{}
json.Unmarshal([]byte(data), &result)
err = PrettyPrint(result)
if err != nil {
return err
}
return nil
}
インTryMarshal
, 私はmap[string]interface{}
データを保持する.私はそれをパスしたMarshal
.イン
TryUnmarshal
, ファイルを読んでバイトスライスに変換しますdata
. あれdata
が渡されるUnmarshal
, を返します.map[string]interface{}
.PrettyPrint
ちょうどそれが素敵に見えるように出力をフォーマットします.では、見てみましょう
Encoder.Encode
and Decoder.Decode
.func TryEncode() error {
data := map[string]interface{}{
"1": "one",
"2": "two",
"3": "three",
}
err := json.NewEncoder(os.Stdout).Encode(&data)
if err != nil {
return err
}
return nil
}
func TryDecode(path string) error {
myFile, err := os.Open(path)
if err != nil {
return err
}
defer myFile.Close()
var result map[string]interface{}
json.NewDecoder(myFile).Decode(&result)
return nil
}
コードは前の例とかなり似ています.TryEncode
似ているTryMarhsal
and TryDecode
似ているTryUnmarshal
. ここだけの違いはEncode
and Decode
メソッドはEncoder
and Decoder
種類NewEncoder
を取り込むio.Writer
インターフェースとEncoder
種類NewDecoder
を取り込むio.Reader
インターフェースとDecoder
種類この例では、os.Stdout
for NewEncoder
and myFile
のos.File
種類NewDecoder
.これらの関数の使い方を知っているので、フードの下で2つのアプローチがどのように異なるかに飛び込むことができます.
marshal ()およびunmarshal ()
の実装を見てみましょう.
func Marshal(v any) ([]byte, error) {
e := newEncodeState()
err := e.marshal(v, encOpts{escapeHTML: true})
if err != nil {
return nil, err
}
buf := append([]byte(nil), e.Bytes()...)
encodeStatePool.Put(e)
return buf, nil
}
func Unmarshal(data []byte, v any) error {
var d decodeState
err := checkValid(data, &d.scan)
if err != nil {
return err
}
d.init(data)
return d.unmarshal(v)
}
ここで知る必要があるのは、Marshal
任意の値をとるany
ラッパアラウンドinterface{}
) バイトスライスに変換します.Unmarshal
バイトスライスを取り、それを解析し、結果をv
. Marshal
すべてのバイトをバイトスライスに格納するbuf
. これはMarshal
すべてのデータをメモリに保持する必要があります.これはかなりのRAM集約することができます.Unmarshal
入力としてバイト全体のスライスを取るので、同様の問題があります.newEncoding ()encode ()およびnewdecode ()decode ()
encoding ()およびdecode ()のコードを示します:
func (enc *Encoder) Encode(v any) error {
if enc.err != nil {
return enc.err
}
e := newEncodeState()
err := e.marshal(v, encOpts{escapeHTML: enc.escapeHTML})
if err != nil {
return err
}
e.WriteByte('\n')
b := e.Bytes()
if enc.indentPrefix != "" || enc.indentValue != "" {
if enc.indentBuf == nil {
enc.indentBuf = new(bytes.Buffer)
}
enc.indentBuf.Reset()
err = Indent(enc.indentBuf, b, enc.indentPrefix, enc.indentValue)
if err != nil {
return err
}
b = enc.indentBuf.Bytes()
}
if _, err = enc.w.Write(b); err != nil {
enc.err = err
}
encodeStatePool.Put(e)
return err
}
func (dec *Decoder) Decode(v any) error {
if dec.err != nil {
return dec.err
}
if err := dec.tokenPrepareForDecode(); err != nil {
return err
}
if !dec.tokenValueAllowed() {
return &SyntaxError{msg: "not at beginning of value", Offset: dec.InputOffset()}
}
n, err := dec.readValue()
if err != nil {
return err
}
dec.d.init(dec.buf[dec.scanp : dec.scanp+n])
dec.scanp += n
err = dec.d.unmarshal(v)
dec.tokenValueEnd()
return err
}
コードはここでは長いですが、これらのことを思い出してください.Encode
and Decode
メソッドはEncoder
and Decoder
人気のインターフェイスの周りのラッパーですio.Writer
and io.Reader
. Encode
and Decode
ストリームデータを一度にすべてを格納するのではなく.そこからバッファがあるEncode
and Decode
書き込みと読み込み、すべてのデータが処理されるまで、これが起こります.……どちらを使うべきですか。
良い質問!私は2つのアプローチのパフォーマンスの違いを見て好奇心旺盛だったので、私はテストを書いて、それらをベンチマーク.任意の印刷は、テストのために無効になっていることに注意してください.
テストのために設計されて
Unmarshal
and Decode
通常、通常、あなたは巨大なJSONデータを書くことができません.一方、サーバーからの巨大なJSONデータを受け取ることができます.あなたはまだ同様の結果を期待することができますMarshal
and Encode
なぜなら、彼らは基本的にパートナーの関数とは逆だからです.以下にテストコードを示します.
func BenchmarkTryUnmarshal(b *testing.B) {
for i := 0; i < b.N; i++ {
err := TryUnmarshal("file.json")
if err != nil {
b.Fatalf("error: %v", err)
}
}
}
func BenchmarkTryDecode(b *testing.B) {
for i := 0; i < b.N; i++ {
err := TryDecode("file.json")
if err != nil {
b.Fatalf("error: %v", err)
}
}
}
"file.json"
は実験変数です.これらはそれぞれの実行のための異なるサイズのJSONファイルになります.最初の5つのJSONファイルはJSONPlaceholder - Free Fake REST API . 最後のJSONファイル(最大のもの)はtest-data/large-file.json at master · json-iterator/test-data · GitHub .ここでは、テストのために使用されるアーキテクチャです.
goos: linux
goarch: amd64
pkg: example.com/jsonExperiment
cpu: Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz
そしてベンチマークデータです.非マーシャル
JSONファイルサイズ
ループの実行
反復回数( ns/op )の時間
操作ごとに割り当てられたバイト数( B/OP )
操作あたりの割り当ての数
7
12819年
96463
56520年
582年
13
7323
155131
112736
1036年
31
3748年
315787
238736
1439年
175
679年
1647401
1300978
8992
1252年
94
12934180
10677126
84603年
25618
4
276607700
237844490
1750110
デコード
JSONファイルサイズ
ループの実行
反復回数( ns/op )の時間
操作ごとに割り当てられたバイト数( B/OP )
操作あたりの割り当ての数
7
13702年
88839
35432
580
13
7882
149191年
79312
1032年
31
4260
280336
149424
1433年
175
760
1583824
965830
8983年
1252年
94
12613618
7491156
84588
25618
4
261644025
166432316
1750103年
ここでいくつかのパターンを見ることができます.
Decode
一貫して以下のメモリを使用しますUnmarshal
. しかし、これは多くの違いではありません.結論
パフォーマンスの違いはかなり小さいので、どのアプローチを取るべきかを決めるときには、パフォーマンスはmakeまたはbreak factorであるべきではないと思います.考慮するより合理的な方法は、あなたがどのようなデータ形式を使っているかを見ることです.例えば、このスニペットを見てください.
func Homepage(w http.ResponseWriter, r *http.Request){
type pageData struct {
visited time.Time
message string
}
homepageData := pageData{time.Now(), "Welcome!"}
json.NewEncoder(w).Encode(&homepageData)
}
func main() {
http.HandleFunc("/", Homepage)
log.Fatal(http.ListenAndServe(":8080", nil))
}
これは、APIがGoでどのように見えるかの簡単な例です.へのどんな要請/
エンドポイントは、このコードを実行するトリガHomepage
のインスタンスを生成するpageData
を使ってエンコードしますNewEncoder(w).Encode(&homepageData)
. w
実装io.Writer
, だから使用する方が便利ですEncode
これはio.Writer
. 構造体をバイトスライスに技術的に変換してからMarshal
. しかし、なぜ必要なときに余分なステップを取る?テイクアウトポイントは、問題になるまでパフォーマンスを心配する必要はありません.代わりに、あなたは、与えられた時間で使用する最も簡単なソリューションを選ぶ必要があります.バイトスライスがあれば使用する
Marshal
and Unmarshal
. があるならばio.Writer
またはio.Reader
, 用途Encode
and Decode
.読んでくれてありがとう!これは私にとって興味深い話題であり、いくつかの実験をしたかった.コメントの下で私を知っている場合は、これらのタイプのポストのように!このポストを読むことができますMedium and my personal site 同様に.
Reference
この問題について(を返します.GOにおけるJSON処理), 我々は、より多くの情報をここで見つけました https://dev.to/jpoly1219/to-unmarshal-or-to-decode-json-processing-in-go-explained-5870テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol