前書き

構造体または構造体は、複数の情報を1つのユニットにまとめるために使用されます。 これらのhttps://www.digitalocean.com/community/tutorials/defining-structs-in-go [情報の収集]は、「+ Street 」で構成される「 Address 」などの上位概念を記述するために使用されます、 ` City `、 ` State `、および ` PostalCode +`。 データベースやAPIなどのシステムからこの情報を読み取る場合、structタグを使用して、この情報をstructのフィールドに割り当てる方法を制御できます。 構造体タグは、構造体のフィールドに付加されたメタデータの小さな断片であり、構造体で動作する他のGoコードに指示を提供します。

Structタグはどのように見えますか?

Go構造体タグは、Go構造体宣言の型の後に表示される注釈です。 各タグは、対応する値に関連付けられた短い文字列で構成されます。

構造体タグは次のようになります。バックティックの「+ \」「」文字でタグをオフセットします。

type User struct {
   Name string `example:"name"`
}

他のGoコードは、これらの構造体を調べて、要求する特定のキーに割り当てられた値を抽出できます。 Structタグは、コードを検査する他のコードがない限り、コードの動作に影響を与えません。

この例を試して、structタグがどのように見えるかを確認してください。また、別のパッケージのコードがなければ、効果はありません。

package main

import "fmt"

type User struct {
   Name string `example:"name"`
}

func (u *User) String() string {
   return fmt.Sprintf("Hi! My name is %s", u.Name)
}

func main() {
   u := &User{
       Name: "Sammy",
   }

   fmt.Println(u)
}

これは出力されます:

OutputHi! My name is Sammy

この例では、「+ Name」フィールドを持つ「+ User」タイプを定義します。 + Name +`フィールドには、 `+ example:" name "+`のstructタグが与えられています。 会話でこの特定のタグを「example struct tag」と呼びます。これは、「example」という単語をキーとして使用するためです。 `+ example + structタグは、 `+ Name `フィールドに対して値 `” name “`を持ちます。 ` User `タイプでは、 ` fmt.Stringer `インターフェースに必要な ` String()`メソッドも定義します。 これは、型を ` fmt.Println +`に渡すと自動的に呼び出され、構造体の適切にフォーマットされたバージョンを生成する機会を与えます。

`+ main `の本体内で、 ` User `タイプの新しいインスタンスを作成し、それを ` fmt.Println +`に渡します。 構造体にstructタグが存在していても、このGoコードの操作には影響がないことがわかります。 structタグが存在しない場合、まったく同じように動作します。

構造体タグを使用して何かを達成するには、実行時に構造体を調べる他のGoコードを記述する必要があります。 標準ライブラリには、操作の一部としてstructタグを使用するパッケージがあります。 これらの中で最も人気のあるものは、 `+ encoding / json +`パッケージです。

JSONのエンコード

JavaScript Object Notation(JSON)は、さまざまな文字列キーで整理されたデータのコレクションをエンコードするためのテキスト形式です。 さまざまなプログラムでデータを通信するために一般的に使用されます。形式が非常に単純なので、多くの異なる言語でデコードできるライブラリが存在します。 以下はJSONの例です。

{
 "language": "Go",
 "mascot": "Gopher"
}

このJSONオブジェクトには、「+ language 」と「 mascot 」の2つのキーが含まれています。 これらのキーに続いて、関連付けられた値があります。 ここで、「 language 」キーの値は「 Go 」であり、「 mascot 」には値「 Gopher +」が割り当てられます。

標準ライブラリのJSONエンコーダーは、JSON出力のフィールドに名前を付ける方法をエンコーダーに示す注釈としてstructタグを使用します。 これらのJSONエンコードおよびデコードメカニズムは、 + encoding / json + packageにあります。

structタグなしでJSONがどのようにエンコードされるかを確認するには、この例を試してください。

package main

import (
   "encoding/json"
   "fmt"
   "log"
   "os"
   "time"
)

type User struct {
   Name          string
   Password      string
   PreferredFish []string
   CreatedAt     time.Time
}

func main() {
   u := &User{
       Name:      "Sammy the Shark",
       Password:  "fisharegreat",
       CreatedAt: time.Now(),
   }

   out, err := json.MarshalIndent(u, "", "  ")
   if err != nil {
       log.Println(err)
       os.Exit(1)
   }

   fmt.Println(string(out))
}

これにより、次の出力が印刷されます。

Output{
 "Name": "Sammy the Shark",
 "Password": "fisharegreat",
 "CreatedAt": "2019-09-23T15:50:01.203059-04:00"
}

名前、パスワード、ユーザーが作成された時刻などのフィールドを持つユーザーを記述する構造体を定義しました。 + main +`関数内で、 `+ PreferredFish +(Sammyはすべての魚が好きです)を除くすべてのフィールドに値を指定することにより、このユーザーのインスタンスを作成します。 次に、 `+ User `のインスタンスを ` json.MarshalIndent `関数に渡しました。 これは、外部のフォーマットツールを使用せずにJSON出力をより簡単に確認できるようにするために使用されます。 この呼び出しは、追加の空白なしでJSONを受信するために ` json.Marshal(u)`に置き換えることができます。 ` json.MarshalIndent `への2つの追加引数は、出力のプレフィックス(空の文字列では省略されています)、およびインデントに使用する文字(ここでは2つのスペース文字)を制御します。 ` json.MarshalIndent `から生成されたエラーはすべてログに記録され、プログラムは ` os.Exit(1)`を使用して終了します。 最後に、 ` json.MarshalIndent `から返された ` [] byte `を ` string `にキャストし、結果の文字列を端末で印刷するために ` fmt.Println +`に渡します。

構造体のフィールドは、指定したとおりに表示されます。 これは、予想される典型的なJSONスタイルではなく、フィールドの名前にキャメルケースを使用します。 次の例では、フィールドの名前をラクダケーススタイルに合わせて変更します。 この例を実行するとわかるように、目的のフィールド名がエクスポートされたフィールド名に関するGoのルールと競合するため、これは機能しません。

package main

import (
   "encoding/json"
   "fmt"
   "log"
   "os"
   "time"
)

type User struct {
   name          string
   password      string
   preferredFish []string
   createdAt     time.Time
}

func main() {
   u := &User{
       name:      "Sammy the Shark",
       password:  "fisharegreat",
       createdAt: time.Now(),
   }

   out, err := json.MarshalIndent(u, "", "  ")
   if err != nil {
       log.Println(err)
       os.Exit(1)
   }

   fmt.Println(string(out))
}

これにより、次の出力が表示されます。

Output{}

このバージョンでは、フィールドの名前をキャメルケースに変更しました。 現在、「+ Name 」は「 name 」、「 Password 」は「 password 」、最後に「 CreatedAt 」は「 createdAt 」です。 ` main `の本文内で、これらの新しい名前を使用するように構造体のインスタンス化を変更しました。 次に、前述のように、構造体を ` json.MarshalIndent `関数に渡します。 出力は、今回は空のJSONオブジェクトである ` {} +`です。

キャメルケーシングフィールドでは、最初の文字を小文字にする必要があります。 JSONはフィールドの名前を気にしませんが、Goはパッケージの外にあるフィールドの可視性を示すため気にしません。 + encoding / json`パッケージは使用している + main + パッケージとは別のパッケージであるため、 + encoding / json`から見えるようにするには最初の文字を大文字にする必要があります。 私たちは行き詰まっているように見えます。このフィールドに名前を付けたいことをJSONエンコーダーに伝える方法が必要です。

Structタグを使用してエンコードを制御する

前の例を修正して、各フィールドにstructタグで注釈を付けることにより、キャメルケースのフィールド名で適切にエンコードされたフィールドをエクスポートすることができます。 `+ encoding / json `が認識するstructタグには、 ` json `のキーと出力を制御する値があります。 ` json +`キーの値としてフィールド名のキャメルケースバージョンを配置することにより、エンコーダーは代わりにその名前を使用します。 この例は、前の2つの試みを修正します。

package main

import (
   "encoding/json"
   "fmt"
   "log"
   "os"
   "time"
)

type User struct {
   Name          string    `json:"name"`
   Password      string    `json:"password"`
   PreferredFish []string  `json:"preferredFish"`
   CreatedAt     time.Time `json:"createdAt"`
}

func main() {
   u := &User{
       Name:      "Sammy the Shark",
       Password:  "fisharegreat",
       CreatedAt: time.Now(),
   }

   out, err := json.MarshalIndent(u, "", "  ")
   if err != nil {
       log.Println(err)
       os.Exit(1)
   }

   fmt.Println(string(out))
}

これは出力されます:

Output{
 "name": "Sammy the Shark",
 "password": "fisharegreat",
 "preferredFish": null,
 "createdAt": "2019-09-23T18:16:17.57739-04:00"
}

名前の最初の文字を大文字にすることで、フィールド名を他のパッケージに表示されるように変更しました。 ただし、今回はstructタグを `+ json:” name “`の形式で追加しました。ここで、 `” name “`はstructを印刷するときに ` json.MarshalIndent +`に使用させたい名前です。 JSON。

JSONが正しくフォーマットされました。 ただし、一部の値のフィールドは、それらの値を設定していなくても印刷されていることに注意してください。 必要に応じて、JSONエンコーダーはこれらのフィールドも削除できます。

空のJSONフィールドを削除する

最も一般的には、JSONで設定されていないフィールドの出力を抑制します。 Goのすべてのタイプには「ゼロ値」、つまりデフォルト値が設定されているため、 `+ encoding / json `パッケージには、このゼロ値を想定したときに一部のフィールドが未設定と見なされることを示す追加情報が必要です。 任意の ` json `構造タグの値部分内で、フィールドがゼロ値に設定されたときにこのフィールドの出力を抑制するようJSONエンコーダーに指示するために、フィールドの目的の名前に `、omitempty +`を付加できます。 次の例は、前の例を修正して空のフィールドを出力しないようにします。

package main

import (
   "encoding/json"
   "fmt"
   "log"
   "os"
   "time"
)

type User struct {
   Name          string    `json:"name"`
   Password      string    `json:"password"`
   PreferredFish []string  `json:"preferredFish,omitempty"`
   CreatedAt     time.Time `json:"createdAt"`
}

func main() {
   u := &User{
       Name:      "Sammy the Shark",
       Password:  "fisharegreat",
       CreatedAt: time.Now(),
   }

   out, err := json.MarshalIndent(u, "", "  ")
   if err != nil {
       log.Println(err)
       os.Exit(1)
   }

   fmt.Println(string(out))
}

この例は出力します:

Output{
 "name": "Sammy the Shark",
 "password": "fisharegreat",
 "createdAt": "2019-09-23T18:21:53.863846-04:00"
}

前の例を修正して、 `+ PreferredFish `フィールドにstructタグ ` json:” preferredFish、omitempty “`が含まれるようにしました。 `、omitempty `拡張の存在により、JSONエンコーダーは未設定のままにすることを決定したため、そのフィールドをスキップします。 これは、前の例の出力では値が「 null」でした。

この出力は見た目は格段に優れていますが、ユーザーのパスワードは引き続き印刷されています。 `+ encoding / json +`パッケージは、プライベートフィールドを完全に無視する別の方法を提供します。

プライベートフィールドを無視する

他のパッケージが型と正しく対話できるように、一部のフィールドは構造体からエクスポートする必要があります。 ただし、これらのフィールドの性質は機密性が高い場合があるため、これらの状況では、JSONエンコーダーが設定されている場合でもフィールドを完全に無視する必要があります。 これは、特別な値 -+`を `+ json: structタグの値引数として使用して行われます。

この例は、ユーザーのパスワードを公開する問題を修正します。

package main

import (
   "encoding/json"
   "fmt"
   "log"
   "os"
   "time"
)

type User struct {
   Name      string    `json:"name"`
   Password  string    `json:"-"`
   CreatedAt time.Time `json:"createdAt"`
}

func main() {
   u := &User{
       Name:      "Sammy the Shark",
       Password:  "fisharegreat",
       CreatedAt: time.Now(),
   }

   out, err := json.MarshalIndent(u, "", "  ")
   if err != nil {
       log.Println(err)
       os.Exit(1)
   }

   fmt.Println(string(out))
}

この例を実行すると、次の出力が表示されます。

Output{
 "name": "Sammy the Shark",
 "createdAt": "2019-09-23T16:08:21.124481-04:00"
}

この例で以前の例から変更した唯一のことは、パスワードフィールドがその + json:+ structタグに特別な `”-“`値を使用するようになったことです。 この例の出力では、「+ password +」フィールドが存在しないことがわかります。

+ encoding / json`パッケージのこれらの機能、 、omitempty`および `”-“`は標準ではありません。 パッケージがstructタグの値をどうするかは、その実装に依存します。 ` encoding / json`パッケージは標準ライブラリの一部であるため、他のパッケージも慣例に従って同じ方法でこれらの機能を実装しています。 ただし、structタグを使用するサードパーティパッケージのドキュメントを読んで、サポート対象とサポート対象を確認することが重要です。

結論

構造体タグは、構造体で機能するコードの機能を強化する強力な手段を提供します。 多くの標準ライブラリおよびサードパーティパッケージには、structタグを使用して操作をカスタマイズする方法が用意されています。 コードでこれらを効果的に使用すると、このカスタマイズ動作と、これらのフィールドが将来の開発者にどのように使用されるかを簡潔に文書化できます。