GoでJSONを使用する方法
著者は、 Diversity in Tech Fund を選択して、 Write forDOnationsプログラムの一環として寄付を受け取りました。
序章
最新のプログラムでは、あるプログラムと別のプログラムの間で通信することが重要です。 ユーザーが別のプログラムにアクセスできるかどうかをチェックするGoプログラム、Webサイトに表示する過去の注文のリストを取得する JavaScript プログラム、または
最新のプログラミング言語の多くには、標準ライブラリでJSONとの間でデータを変換する方法が含まれています。Goも同様です。 Goが提供するencoding/ json パッケージを使用することで、GoプログラムはJSONを使用して通信できる他のシステムとも対話できるようになります。
このチュートリアルでは、encoding/json
パッケージを使用してmap
からJSONデータにデータをエンコードするプログラムを作成し、次にstruct
タイプを使用するようにプログラムを更新します。代わりにデータをエンコードします。 その後、JSONデータをmap
にデコードするようにプログラムを更新してから、最終的にJSONデータをstruct
タイプにデコードします。
前提条件
このチュートリアルに従うには、次のものが必要です。
- Goバージョン1.16以降がインストールされています。これは、シリーズGoのローカルプログラミング環境をインストールおよびセットアップする方法に従って実行できます。
- JSONの概要にあるJSONの知識。
- Go構造体タグを使用して
struct
タイプのフィールドをカスタマイズする機能。 詳細については、Goで構造体タグを使用する方法を参照してください。 - 必要に応じて、Goで日付と時刻の値を作成する方法に注意してください。 詳細については、Goで日付と時刻を使用する方法を参照してください。
マップを使用してJSONを生成する
JSONのエンコードとデコードに対するGoのサポートは、標準ライブラリのencoding/json
パッケージによって提供されます。 そのパッケージから使用する最初の関数は、json.Marshal関数です。 マーシャリングは、シリアル化とも呼ばれ、メモリ内のプログラムデータを他の場所で送信または保存できる形式に変換するプロセスです。 次に、json.Marshal
関数を使用して、GoデータをJSONデータに変換します。 json.Marshal
関数は、JSONにマーシャリングする値としてinterface{}
型を受け入れるため、任意の値をパラメーターとして渡すことができ、結果としてJSONデータを返します。 このセクションでは、json.Marshal
関数を使用して、Go map
値からさまざまなタイプのデータを含むJSONを生成し、それらの値を出力に出力するプログラムを作成します。
ほとんどのJSONはオブジェクトとして表され、string
キーとその他のさまざまなタイプが値として使用されます。 このため、GoでJSONデータを生成する最も柔軟な方法は、string
キーとinterface{}
値を使用してデータをmap
に配置することです。 string
キーはJSONオブジェクトキーに直接変換でき、interface{}
値を使用すると、string
、int
、または別のmap[string]interface{}
。
プログラムでencoding/json
パッケージの使用を開始するには、プログラム用のディレクトリが必要です。 このチュートリアルでは、projects
という名前のディレクトリを使用します。
まず、projects
ディレクトリを作成し、次の場所に移動します。
- mkdir projects
- cd projects
次に、プロジェクトのディレクトリを作成します。 この場合、ディレクトリjsondata
を使用します。
- mkdir jsondata
- cd jsondata
jsondata
ディレクトリ内で、nano
またはお気に入りのエディタを使用して、main.go
ファイルを開きます。
- nano main.go
main.go
ファイルに、main
関数を追加してプログラムを実行します。 次に、さまざまなキーとデータの種類を使用してmap[string]interface{}
値を追加します。 次に、json.Marshal
関数を使用して、map
データをJSONデータにマーシャリングします。
main.go
に次の行を追加します。
package main
import (
"encoding/json"
"fmt"
)
func main() {
data := map[string]interface{}{
"intValue": 1234,
"boolValue": true,
"stringValue": "hello!",
"objectValue": map[string]interface{}{
"arrayValue": []int{1, 2, 3, 4},
},
}
jsonData, err := json.Marshal(data)
if err != nil {
fmt.Printf("could not marshal json: %s\n", err)
return
}
fmt.Printf("json data: %s\n", jsonData)
}
data
変数には、各値のキーとしてstring
が含まれていることがわかりますが、これらのキーの値は異なります。 1つはint
値、もう1つはbool
値、もう1つは[]int
値を含む別のmap[string]interface{}
値です。
data
変数をjson.Marshal
に渡すと、関数は指定されたすべての値を調べて、それらがどのタイプであり、JSONでそれらをどのように表現するかを決定します。 変換に問題がある場合、json.Marshal
関数は問題を説明するerror
を返します。 ただし、変換が成功した場合、jsonData
変数にはマーシャリングされたJSONデータの[]byte
が含まれます。 []byte
値は、myString := string(jsonData)
、またはフォーマット文字列の%s
動詞を使用して、string
値に変換できるため、次に、fmt.Printf
を使用してJSONデータを画面に出力できます。
ファイルを保存して閉じます。
プログラムの出力を確認するには、go run
コマンドを使用して、main.go
ファイルを提供します。
- go run main.go
出力は次のようになります。
Outputjson data: {"boolValue":true,"intValue":1234,"objectValue":{"arrayValue":[1,2,3,4]},"stringValue":"hello!"}
出力では、最上位のJSON値が、それを囲む中括弧({}
)で表されるオブジェクトであることがわかります。 data
に含めたすべての値が存在します。 また、objectValue
のmap[string]interface{}
が{}
で囲まれた別のJSONオブジェクトに変換され、配列値とともにarrayValue
が含まれていることもわかります。 [1,2,3,4]
の。
JSONでのエンコード時間
ただし、encoding/json
パッケージは、string
やint
の値などのタイプをサポートするだけではありません。 また、より複雑なタイプをエンコードすることもできます。 サポートされているより複雑なタイプの1つは、timeパッケージのtime.Time
タイプです。
注:Goのtime
パッケージの詳細については、チュートリアルGoで日付と時刻を使用する方法を確認してください。
これが実際に動作することを確認するには、main.go
ファイルを再度開き、time.Date
関数を使用してtime.Time
値をデータに追加します。
package main
import (
"encoding/json"
"fmt"
"time"
)
func main() {
data := map[string]interface{}{
"intValue": 1234,
"boolValue": true,
"stringValue": "hello!",
"dateValue": time.Date(2022, 3, 2, 9, 10, 0, 0, time.UTC),
"objectValue": map[string]interface{}{
"arrayValue": []int{1, 2, 3, 4},
},
}
...
}
この更新により、UTC
タイムゾーンの日付March 2, 2022,
と時刻9:10:00 AM
がdateValue
キーに割り当てられます。
変更を保存したら、前と同じgo run
コマンドを使用してプログラムを再実行します。
- go run main.go
出力は次のようになります。
Outputjson data: {"boolValue":true,"dateValue":"2022-03-02T09:10:00Z","intValue":1234,"objectValue":{"arrayValue":[1,2,3,4]},"stringValue":"hello!"}
今回の出力では、JSONデータにdateValue
フィールドが表示され、時刻は RFC 3339 形式でフォーマットされています。これは、日付と時刻をstring
値。
null
値をJSONでエンコードする
プログラムが対話するシステムによっては、JSONデータでnull
値を送信する必要がある場合があり、Goのencoding/json
パッケージでもそれを処理できます。 map
を使用すると、nil
値を持つ新しいstring
キーを追加するだけです。
いくつかのnull
値をJSON出力に追加するには、main.go
ファイルを再度開き、次の行を追加します。
...
func main() {
data := map[string]interface{}{
"intValue": 1234,
"boolValue": true,
"stringValue": "hello!",
"dateValue": time.Date(2022, 3, 2, 9, 10, 0, 0, time.UTC),
"objectValue": map[string]interface{}{
"arrayValue": []int{1, 2, 3, 4},
},
"nullStringValue": nil,
"nullIntValue": nil,
}
...
}
データに追加した値には、string
値またはint
値であるというキーがありますが、実際には、これらの値のいずれかを作成しているコードはありません。 map
にはinterface{}
の値があるため、コードはinterface{}
の値がnil
であることを知っています。 このmap
を使用してGoデータからJSONデータに変換するだけなので、この時点での区別は違いはありません。
main.go
に変更を保存したら、go run
を使用してプログラムを実行します。
- go run main.go
出力は次のようになります。
Outputjson data: {"boolValue":true,"dateValue":"2022-03-02T09:10:00Z","intValue":1234,"nullIntValue":null,"nullStringValue":null,"objectValue":{"arrayValue":[1,2,3,4]},"stringValue":"hello!"}
出力には、nullIntValue
フィールドとnullStringValue
フィールドがJSONnull
値に含まれていることがわかります。 このようにして、map[string]interface{}
値を使用して、Goデータを期待されるフィールドを持つJSONデータに変換できます。
このセクションでは、map[string]interface{}
値をJSONデータにマーシャリングできるプログラムを作成しました。 次に、time.Time
フィールドをデータに追加し、null
値フィールドのペアも含めました。
map[string]interface{}
を使用してJSONデータをマーシャリングすることは非常に柔軟ですが、同じデータを複数の場所に送信する必要がある場合は面倒になる可能性もあります。 このデータをコード内の複数の場所にコピーすると、誤ってフィールド名を誤って入力したり、誤ったデータをフィールドに割り当てたりする可能性があります。 このような場合は、struct
タイプを使用して、JSONに変換するデータを表すと便利な場合があります。
Structを使用してJSONを生成する
Goのような静的に型付けされた言語を使用する利点の1つは、これらの型を使用して、コンパイラーにプログラムの整合性をチェックまたは強制させることができることです。 Goのencoding/json
パッケージでは、JSONデータを表すstruct
タイプを定義することで、これを利用できます。 struct
に含まれるデータが、構造体タグを使用してどのように変換されるかを制御できます。 このセクションでは、map
タイプの代わりにstruct
タイプを使用してJSONデータを生成するようにプログラムを更新します。
struct
を使用してJSONデータを定義する場合、翻訳されると予想されるフィールド名(struct
タイプ名自体ではない)をエクスポートする必要があります。つまり、大文字で始まる必要があります。 IntValue
、またはencoding/json
パッケージは、フィールドにアクセスしてJSONに変換することができなくなります。 これらのフィールドの名前を制御するために構造体タグを使用しない場合、フィールド名はstruct
の場合と同じように直接変換されます。 データの形成方法によっては、デフォルトの名前を使用することがJSONデータで希望する場合があります。 この場合、構造体タグを追加する必要はありません。 ただし、多くのJSONコンシューマーは、フィールド名にintValue
やint_value
などの名前形式を使用するため、これらの構造体タグを追加すると、その変換の実行方法を制御できます。
たとえば、JSONにマーシャリングしたIntValue
というフィールドを持つstruct
があるとします。
type myInt struct {
IntValue int
}
data := &myInt{IntValue: 1234}
json.Marshal
関数を使用してdata
変数をJSONにマーシャリングした場合、次の値になります。
{"IntValue":1234}
ただし、JSONコンシューマーが、フィールドの名前がIntValue
ではなくintValue
であることを期待している場合は、encoding/json
を通知する方法が必要になります。 json.Marshal
は、JSONデータでフィールドの名前が何であるかを認識していないため、フィールドに構造体タグを追加することで通知します。 json
構造体タグをIntValue
フィールドにintValue
の値で追加することにより、json.Marshal
にintValue
という名前を使用するように指示します。 JSONデータを生成する場合:
type myInt struct {
IntValue int `json:"intValue"`
}
data := &myInt{IntValue: 1234}
今回、data
変数をJSONにマーシャリングすると、json.Marshal
関数はjson
構造体タグを認識し、フィールドにintValue
という名前を付けることができます。期待どおりの結果が得られます:
{"intValue":1234}
次に、JSONデータにstruct
値を使用するようにプログラムを更新します。 myJSON
struct
タイプを追加してトップレベルのJSONオブジェクトを定義し、myObject
struct
を追加して内部JSONオブジェクトを定義します。 ObjectValue
フィールド。 また、json
構造体タグを各フィールドに追加して、json.Marshal
にJSONデータでの名前の付け方を指示します。 また、data
変数の割り当てを更新して、myJSON
構造体を使用し、他のGostruct
と同様に宣言する必要があります。
main.go
ファイルを開き、次の変更を加えます。
...
type myJSON struct {
IntValue int `json:"intValue"`
BoolValue bool `json:"boolValue"`
StringValue string `json:"stringValue"`
DateValue time.Time `json:"dateValue"`
ObjectValue *myObject `json:"objectValue"`
NullStringValue *string `json:"nullStringValue"`
NullIntValue *int `json:"nullIntValue"`
}
type myObject struct {
ArrayValue []int `json:"arrayValue"`
}
func main() {
otherInt := 4321
data := &myJSON{
IntValue: 1234,
BoolValue: true,
StringValue: "hello!",
DateValue: time.Date(2022, 3, 2, 9, 10, 0, 0, time.UTC),
ObjectValue: &myObject{
ArrayValue: []int{1, 2, 3, 4},
},
NullStringValue: nil,
NullIntValue: &otherInt,
}
...
}
これらの変更の多くは、以前のIntValue
フィールド名の例に似ていますが、一部の変更は具体的に呼び出す必要があります。 それらの1つであるObjectValue
フィールドは、*myObject
の参照型を使用して、JSONマーシャラーにmyObject
値またはmyObject
タイプ内で別のstruct
タイプを参照することもできます。 このパターンを使用すると、Gostruct
タイプを使用して非常に複雑なJSONオブジェクトを記述できます。
上記のコードで確認するもう1つのフィールドのペアは、NullStringValue
とNullIntValue
です。 StringValue
およびIntValue
とは異なり、これらの値のタイプは参照タイプ*string
および*int
です。 デフォルトでは、string
およびint
タイプは、「空の」値が""
および0
であるため、nil
の値を持つことはできません。 したがって、1つのタイプまたはnil
のいずれかであるフィールドを表す場合は、それを参照にする必要があります。 たとえば、ユーザーアンケートがあり、ユーザーが質問に回答しないことを選択した場合(null
の値)を表現できるようにしたいとします。 ""
値)。
このコードは、NullIntValue
フィールドを更新して、4321
の値を割り当て、*int
などの参照型に値を割り当てる方法を示します。 Goでは、変数を使用して、int
やstring
などのプリミティブ型への参照のみを作成できます。 したがって、NullIntValue
フィールドに値を割り当てるには、最初に値を別の変数otherInt
に割り当て、次に&otherInt
を使用してその値への参照を取得します(代わりに&4321
を直接実行すること)。
更新を保存したら、go run
を使用してプログラムを実行します。
- go run main.go
出力は次のようになります。
Outputjson data: {"intValue":1234,"boolValue":true,"stringValue":"hello!","dateValue":"2022-03-02T09:10:00Z","objectValue":{"arrayValue":[1,2,3,4]},"nullStringValue":null,"nullIntValue":4321}
この出力は、map[string]interface{}
値を使用した場合と同じですが、今回はnullIntValue
の値が4321
であるため、
最初は、struct
値の設定に余分な時間がかかる場合がありますが、一度定義すると、コードで何度も使用できるようになり、どこで使用しても結果は同じになります。彼ら。 map
が使用される可能性のあるすべての場所を検索する代わりに、それらを1つの場所で更新することもできます。
GoのJSONマーシャラーでは、値が空かどうかに基づいて、フィールドをJSON出力に含めるかどうかを制御することもできます。 大きなJSONオブジェクトや、常に含めたくないオプションのフィールドがある場合があるため、これらのフィールドを省略すると便利です。 フィールドが空のときに省略されるかどうかの制御は、json
構造体タグのomitempty
オプションを介して行われます。
次に、プログラムを更新してNullStringValue
フィールドをomitempty
にし、同じオプションでEmptyString
という新しいフィールドを追加します。
...
type myJSON struct {
...
NullStringValue *string `json:"nullStringValue,omitempty"`
NullIntValue *int `json:"nullIntValue"`
EmptyString string `json:"emptyString,omitempty"`
}
...
これで、myJSON
がマーシャリングされるときに、EmptyString
フィールドとNullStringValue
フィールドの両方の値が空の場合、それらのフィールドは出力から除外されます。
変更を保存したら、go run
を使用してプログラムを実行します。
- go run main.go
出力は次のようになります。
Outputjson data: {"intValue":1234,"boolValue":true,"stringValue":"hello!","dateValue":"2022-03-02T09:10:00Z","objectValue":{"arrayValue":[1,2,3,4]},"nullIntValue":4321}
今回の出力では、nullStringValue
フィールドが表示されなくなります。 nil
の値を持つことで空と見なされるため、omitempty
オプションはそれを出力から除外しました。 また、新しいemptyString
フィールドも含まれていないことがわかります。 emptyString
の値はnil
ではありませんが、文字列のデフォルトの""
値は空であると見なされるため、同様に除外されました。
このセクションでは、struct
タイプを使用して、map
タイプの代わりにjson.Marshal
でJSONデータを生成するようにプログラムを更新しました。 また、JSON出力から空のフィールドを省略するようにプログラムを更新しました。
ただし、プログラムをJSONエコシステムにうまく適合させるには、JSONデータを生成するだけでは不十分です。 また、リクエストに応じて送信されるJSONデータ、またはリクエストを送信する他のシステムを読み取ることができる必要があります。 encoding/json
パッケージは、JSONデータをさまざまなGoタイプにデコードする方法も提供します。 次のセクションでは、JSON文字列をGomap
タイプにデコードするようにプログラムを更新します。
マップを使用したJSONの解析
このチュートリアルの最初のセクションで、JSONデータを生成するための柔軟な方法としてmap[string]interface{}
を使用したのと同様に、JSONデータを読み取るための柔軟な方法としても使用できます。 json.Unmarshal
関数は、本質的にjson.Marshal
関数の反対であり、JSONデータを取得してGoデータに変換し直します。 json.Unmarshal
にJSONデータと、マーシャリングされていないデータを入れるGo変数を指定すると、error
値を返すか、json.Unmarshal
関数を使用して、事前定義されたstring
値からmap
変数にJSONデータを読み取るようにプログラムを更新します。 また、プログラムを更新して、Goデータを出力に出力します。
次に、json.Unmarshal
を使用してJSONデータをmap[string]interface{}
にアンマーシャリングするようにプログラムを更新します。 まず、元のdata
変数をJSON文字列を含むjsonData
変数に置き換えます。 次に、新しいdata
変数をmap[string]interface{}
として宣言して、JSONデータを受信します。 最後に、これらの変数でjson.Unmarshal
を使用して、JSONデータにアクセスします。
main.go
ファイルを開き、main
関数の行を次のように置き換えます。
...
func main() {
jsonData := `
{
"intValue":1234,
"boolValue":true,
"stringValue":"hello!",
"dateValue":"2022-03-02T09:10:00Z",
"objectValue":{
"arrayValue":[1,2,3,4]
},
"nullStringValue":null,
"nullIntValue":null
}
`
var data map[string]interface{}
err := json.Unmarshal([]byte(jsonData), &data)
if err != nil {
fmt.Printf("could not unmarshal json: %s\n", err)
return
}
fmt.Printf("json map: %v\n", data)
}
このアップデートでは、jsonData
変数が生の文字列リテラルを使用して設定され、宣言が複数行にまたがって読みやすくなっています。 data
をmap[string]interface{}
として宣言した後、jsonData
とdata
をjson.Unmarshal
に渡して、JSONデータを
jsonData
変数は[]byte
としてjson.Unmarshal
に渡されます。これは、関数が[]byte
タイプを必要とし、jsonData
が最初にstring
タイプ。 これが機能するのは、Goのstring
を[]byte
に、またはその逆に変換できるためです。 data
変数が参照として渡されているのは、json.Unmarshal
がデータを変数に入れるために、変数がメモリ内のどこに格納されているかへの参照が必要だからです。
最後に、JSONデータがdata
変数にアンマーシャリングされたら、fmt.Printf
を使用して画面に出力します。
更新したプログラムを実行するには、変更を保存し、go run
を使用してプログラムを実行します。
- go run main.go
出力は次のようになります。
Outputjson map: map[boolValue:true dateValue:2022-03-02T09:10:00Z intValue:1234 nullIntValue:<nil> nullStringValue:<nil> objectValue:map[arrayValue:[1 2 3 4]] stringValue:hello!]
今回の出力は、JSON変換のGo側を示しています。 map
値があり、JSONデータのさまざまなフィールドが含まれています。 JSONデータのnull
フィールドもマップに表示されます。
Goデータはmap[string]interface{}
にあるため、データの使用に必要な作業が少しあります。 目的のstring
キー値を使用してmap
から値を取得する必要があります。次に、受け取った値が[Xとして返されるため、期待した値であることを確認する必要があります。 X199X]