GoでJSONを使用する方法

提供:Dev Guides
移動先:案内検索

著者は、 Diversity in Tech Fund を選択して、 Write forDOnationsプログラムの一環として寄付を受け取りました。

序章

最新のプログラムでは、あるプログラムと別のプログラムの間で通信することが重要です。 ユーザーが別のプログラムにアクセスできるかどうかをチェックするGoプログラム、Webサイトに表示する過去の注文のリストを取得する JavaScript プログラム、または Rust [X183X ]プログラムはファイルからテスト結果を読み取ります。プログラムには、他のプログラムにデータを提供する方法が必要です。 ただし、多くのプログラミング言語には、他の言語が理解できない独自のデータを内部に格納する方法があります。 これらの言語が相互作用できるようにするには、データをすべての言語が理解できる共通の形式に変換する必要があります。 これらの形式の1つであるJSONは、インターネット上および同じシステム内のプログラム間でデータを送信するための一般的な方法です。

最新のプログラミング言語の多くには、標準ライブラリでJSONとの間でデータを変換する方法が含まれています。Goも同様です。 Goが提供するencoding/ json パッケージを使用することで、GoプログラムはJSONを使用して通信できる他のシステムとも対話できるようになります。

このチュートリアルでは、encoding/jsonパッケージを使用してmapからJSONデータにデータをエンコードするプログラムを作成し、次にstructタイプを使用するようにプログラムを更新します。代わりにデータをエンコードします。 その後、JSONデータをmapにデコードするようにプログラムを更新してから、最終的にJSONデータをstructタイプにデコードします。

前提条件

このチュートリアルに従うには、次のものが必要です。

  • Go version 1.16 or greater installed. To set this up, follow the How To Install Go tutorial for your operating system.
  • 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{}値を使用すると、stringint、または別の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に次の行を追加します。

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に含めたすべての値が存在します。 また、objectValuemap[string]interface{}{}で囲まれた別のJSONオブジェクトに変換され、配列値とともにarrayValueが含まれていることもわかります。 [1,2,3,4]の。

JSONでのエンコード時間

ただし、encoding/jsonパッケージは、stringintの値などのタイプをサポートするだけではありません。 また、より複雑なタイプをエンコードすることもできます。 サポートされているより複雑なタイプの1つは、timeパッケージのtime.Timeタイプです。

注:Goのtimeパッケージの詳細については、チュートリアルGoで日付と時刻を使用する方法を確認してください。


これが実際に動作することを確認するには、main.goファイルを再度開き、time.Date関数を使用してtime.Time値をデータに追加します。

main.go

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 AMdateValueキーに割り当てられます。

変更を保存したら、前と同じ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ファイルを再度開き、次の行を追加します。

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コンシューマーは、フィールド名にintValueint_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.MarshalintValueという名前を使用するように指示します。 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オブジェクトを定義し、myObjectstructを追加して内部JSONオブジェクトを定義します。 ObjectValueフィールド。 また、json構造体タグを各フィールドに追加して、json.MarshalにJSONデータでの名前の付け方を指示します。 また、data変数の割り当てを更新して、myJSON構造体を使用し、他のGostructと同様に宣言する必要があります。

main.goファイルを開き、次の変更を加えます。

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値または[のいずれかへの参照を期待するように指示しています。 X160X]値。 これは、カスタムオブジェクトの複数のレイヤーであるJSONオブジェクトを定義する方法です。 JSONデータで必要な場合は、myObjectタイプ内で別のstructタイプを参照することもできます。 このパターンを使用すると、Gostructタイプを使用して非常に複雑なJSONオブジェクトを記述できます。

上記のコードで確認するもう1つのフィールドのペアは、NullStringValueNullIntValueです。 StringValueおよびIntValueとは異なり、これらの値のタイプは参照タイプ*stringおよび*intです。 デフォルトでは、stringおよびintタイプは、「空の」値が""および0であるため、nilの値を持つことはできません。 したがって、1つのタイプまたはnilのいずれかであるフィールドを表す場合は、それを参照にする必要があります。 たとえば、ユーザーアンケートがあり、ユーザーが質問に回答しないことを選択した場合(nullの値)を表すことができるようにしたいとします。 ""値)。

このコードは、NullIntValueフィールドを更新して、4321の値を割り当て、*intなどの参照型に値を割り当てる方法を示します。 Goでは、変数を使用して、intstringなどのプリミティブタイプへの参照のみを作成できます。 したがって、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であるため、[の値であることがわかります。 X155X]。

最初は、struct値の設定に余分な時間がかかる場合がありますが、一度定義すると、コードで何度も使用できるようになり、どこで使用しても結果は同じになります。彼ら。 mapが使用される可能性のあるすべての場所を検索する代わりに、それらを1つの場所で更新することもできます。

GoのJSONマーシャラーでは、値が空かどうかに基づいて、フィールドをJSON出力に含めるかどうかを制御することもできます。 大きなJSONオブジェクトや、常に含めたくないオプションのフィールドがある場合があるため、これらのフィールドを省略すると便利です。 フィールドが空のときに省略されるかどうかの制御は、json構造体タグのomitemptyオプションを介して行われます。

次に、プログラムを更新してNullStringValueフィールドをomitemptyにし、同じオプションでEmptyStringという新しいフィールドを追加します。

main.go

...

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値を返すか、[ X181X]成功した場合のエラー値。 このセクションでは、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関数の行を次のように置き換えます。

main.go

...

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変数が生の文字列リテラルを使用して設定され、宣言が複数行にまたがって読みやすくなっています。 datamap[string]interface{}として宣言した後、jsonDatadatajson.Unmarshalに渡して、JSONデータを[にアンマーシャリングします。 X128X]変数。

この関数には[]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]値。

これを行うには、main.goファイルを開き、プログラムを更新して、次のコードでdateValueフィールドを読み取ります。

main.go

...

func main() {
    ...
    
    fmt.Printf("json map: %v\n", data)

    rawDateValue, ok := data["dateValue"]
    if !ok {
        fmt.Printf("dateValue does not exist\n")
        return
    }
    dateValue, ok := rawDateValue.(string)
    if !ok {
        fmt.Printf("dateValue is not a string\n")
        return
    }
    fmt.Printf("date value: %s\n", dateValue)
}

このアップデートでは、data["dateValue"]を使用してrawDateValueinterface{}タイプとして取得し、ok変数を使用して[X129X ]フィールドはmapにあります。

次に、 type assertion を使用して、rawDateValueのタイプが実際にはstring値であることを表明し、それを変数dateValueに割り当てます。 その後、ok変数を再度使用して、アサーションが成功したことを確認します。

最後に、fmt.Printfを使用してdateValueを印刷します。

更新したプログラムを再度実行するには、変更を保存し、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!]
date value: 2022-03-02T09:10:00Z

mapから抽出されてstring値に変換されたdateValueフィールドを示すdate value行が表示されます。

このセクションでは、json.Unmarshal関数とmap[string]interface{}変数を使用して、JSONデータをGoデータにアンマーシャリングするようにプログラムを更新しました。 次に、プログラムを更新して、GoデータからdateValueの値を抽出し、画面に出力しました。

ただし、この更新では、map[string]interface{}を使用してGoでJSONをアンマーシャリングすることの欠点の1つが示されています。 Goは、各フィールドがどのタイプのデータであるかを知らないため(interface{}であることがわかっているだけです)、データを非整列化するためにできる最善のことは、最善の推測を行うことです。 つまり、dateValueフィールドのtime.Timeのような複雑な値は、マーシャリングを解除できず、stringとしてのみアクセスできます。 この方法でmapの任意の数値にアクセスしようとすると、同様の問題が発生します。 json.Unmarshalは、番号がintfloatint64などのいずれであるかを認識していないため、推測できる最善の方法です。利用可能な最も柔軟な数値タイプ、float64にそれを入れることです。

mapを使用してJSONデータをデコードすることは柔軟ですが、データを解釈する際の作業も多くなります。 json.Marshal関数がstruct値を使用してJSONデータを生成する方法と同様に、json.Unmarshal関数はstruct値を使用してJSONデータを読み取ることができます。 これは、structのフィールドの型定義を使用して、JSONデータをどの型として解釈するかを決定することにより、mapを使用する際の型アサーションの複雑さを取り除くのに役立ちます。 次のセクションでは、structタイプを使用してこれらの複雑さを取り除くように、プログラムを更新します。

Structを使用したJSONの解析

JSONデータを読んでいるときは、受け取っているデータの構造をすでに知っている可能性があります。 そうしないと、解釈が困難になります。 この構造に関する知識を使用して、データがどのように見えるか、および期待するデータのタイプについてGoにヒントを与えることができます。

前のセクションでは、myJSONmyObject structの値を定義し、json構造体タグを追加して、JSONを生成するときにフィールドに名前を付ける方法をGoに知らせました。 。 これで、同じstruct値を使用して、使用していたJSON文字列をデコードできます。これは、同じJSONデータをマーシャリングおよびアンマーシャリングする場合に、プログラム内の重複コードを減らすのに役立ちます。 JSONデータのマーシャリングを解除するためにstructを使用するもう1つの利点は、各フィールドに期待されるデータのタイプをGoに伝えることができることです。 最後に、Goのコンパイラを使用して、map値で使用するstring値のタイプミスを見逃すのではなく、フィールドで正しい名前を使用していることを確認することもできます。 。

次に、main.goファイルを開き、data変数宣言を更新して、myJSON structへの参照を使用し、fmt.Printfをいくつか追加します。 ] myJSONのさまざまなフィールドのデータを表示する行:

main.go

...

func main() {
    ...
    
    var data *myJSON
    err := json.Unmarshal([]byte(jsonData), &data)
    if err != nil {
        fmt.Printf("could not unmarshal json: %s\n", err)
        return
    }

    fmt.Printf("json struct: %#v\n", data)
    fmt.Printf("dateValue: %#v\n", data.DateValue)
    fmt.Printf("objectValue: %#v\n", data.ObjectValue)
}

以前にstructタイプを定義したので、structへのアンマーシャリングをサポートするには、dataフィールドのタイプを更新するだけで済みます。 残りの更新では、struct自体のデータの一部が表示されます。

次に、更新を保存し、go runを使用してプログラムを実行します。

go run main.go

出力は次のようになります。

Outputjson struct: &main.myJSON{IntValue:1234, BoolValue:true, StringValue:"hello!", DateValue:time.Date(2022, time.March, 2, 9, 10, 0, 0, time.UTC), ObjectValue:(*main.myObject)(0x1400011c180), NullStringValue:(*string)(nil), NullIntValue:(*int)(nil), EmptyString:""}
dateValue: time.Date(2022, time.March, 2, 9, 10, 0, 0, time.UTC)
objectValue: &main.myObject{ArrayValue:[]int{1, 2, 3, 4}}

今回の出力には、注意すべき点がいくつかあります。 json struct行とdateValue行の両方で、JSONデータの日付値がtime.Time値([X155X ] formatは、%#vがフォーマット動詞として使用されている場合に表示されるものです。 GoはmyJSONDateValueフィールドでtime.Timeタイプを確認できたため、string値も解析できました。

もう1つ注意すべき点は、EmptyStringは、元のJSONデータに含まれていなくても、json struct行に表示されることです。 JSONアンマーシャリングに使用されるstructにフィールドが含まれていて、アンマーシャリングされるJSONデータに含まれていない場合、そのフィールドはそのタイプのデフォルト値に設定され、無視されます。 このようにして、プロセスのどちらの側にもフィールドが存在しない場合にエラーが発生することを心配することなく、JSONデータが持つ可能性のあるすべてのフィールドを安全に定義できます。 NullStringValueNullIntValueの両方も、値がnullであるとJSONデータが示しているため、デフォルト値のnilに設定されますが、これらも次のように設定されます。 nilこれらのフィールドがJSONデータから除外されていた場合。

emptyStringフィールドがJSONデータから欠落しているときにstructEmptyStringフィールドがjson.Unmarshalによって無視されたのと同様に、逆も当てはまります。 フィールドがJSONデータに含まれているが、Go structに対応するフィールドがない場合、そのJSONフィールドは無視され、解析は次のJSONフィールドに進みます。 このように、読み取っているJSONデータが非常に大きく、プログラムがそれらのフィールドの少数のみを考慮している場合は、関心のあるフィールドのみを含むstructを作成することを選択できます。 structで定義されていないJSONデータに含まれるフィールドはすべて無視され、GoのJSONパーサーは次のフィールドに進みます。

これが実際に動作することを確認するには、main.goファイルをもう一度開き、jsonDataを更新して、myJSONに含まれていないフィールドを含めます。

main.go

...

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,
            "extraValue":4321
        }
    `

    ...
}

JSONデータを追加したら、ファイルを保存し、go runを使用して実行します。

go run main.go

出力は次のようになります。

Outputjson struct: &main.myJSON{IntValue:1234, BoolValue:true, StringValue:"hello!", DateValue:time.Date(2022, time.March, 2, 9, 10, 0, 0, time.UTC), ObjectValue:(*main.myObject)(0x14000126180), NullStringValue:(*string)(nil), NullIntValue:(*int)(nil), EmptyString:""}
dateValue: time.Date(2022, time.March, 2, 9, 10, 0, 0, time.UTC)
objectValue: &main.myObject{ArrayValue:[]int{1, 2, 3, 4}}

GoはJSONデータのextraValueフィールドを無視して続行するため、この出力と前の出力の間に違いは見られないはずです。

このセクションでは、JSONデータをアンマーシャリングするために以前に定義したstructタイプを使用するようにプログラムを更新しました。 Goがtime.Time値を解析し、structタイプで定義されているがJSONデータでは定義されていないEmptyStringフィールドを無視する方法を確認しました。 また、JSONデータにフィールドのサブセットのみを定義した場合でも、Goが安全にデータの解析を続行することを確認するために、JSONデータにフィールドを追加しました。

結論

このチュートリアルでは、Goの標準ライブラリにあるencoding/jsonパッケージを使用するための新しいプログラムを作成しました。 まず、json.Marshal関数とmap[string]interface{}タイプを使用して、柔軟な方法でJSONデータを作成しました。 次に、json構造体タグでstructタイプを使用するようにプログラムを更新し、json.Marshalで一貫性のある信頼できる方法でJSONデータを生成しました。 その後、json.Unmarshal関数とmap[string]interface{}タイプを使用して、JSON文字列をGoデータにデコードしました。 最後に、json.Unmarshal関数で以前に定義したstruct型を使用して、これらのstructフィールドに基づいてGoに解析と型変換を実行させました。

encoding/jsonパッケージを使用すると、インターネットで利用可能な多くのAPIと対話して、人気のあるWebサイトとの独自の統合を作成できます。 また、独自のプログラムのGoデータを保存できる形式に変換し、後でロードして、プログラムが中断したところから続行することもできます。

このチュートリアルで使用した関数に加えて、 encoding / json パッケージには、JSONとの対話に使用できる他の便利な関数とタイプが含まれています。 たとえば、 json.MarshalIndent 関数を使用して、トラブルシューティングのためにJSONデータをきれいに印刷することができます。

このチュートリアルは、 DigitalOcean How to Code inGoシリーズの一部でもあります。 このシリーズでは、Goの初めてのインストールから、言語自体の使用方法まで、Goに関する多くのトピックを取り上げています。