🗂

Go言語 jsonタグ

2024/09/22に公開

はじめに

このページではGo言語での以下の内容について記述します。

  • JSONタグの基本
  • オプションタグ
  • JSONのエンコードとデコード

GolangのJSONタグとは

Golangの構造体フィールドにタグを付与することで、JSONエンコード・デコード時にフィールドがどのようにマッピングされるかを制御できます。特にencoding/jsonパッケージを使用してデータを操作する際に、非常に役立ちます。

基本的なJSONタグの使い方

Golangの構造体にJSONタグを追加することで、JSONのキー名や挙動をカスタマイズできます。タグの基本的な形式は次の通りです。

type 構造体名 struct {
    フィールド名 型 `json:"キー名,オプション"`
}

例えば、次のように使います。

type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
}

この場合、IDフィールドは"id"というキーに、Nameフィールドは"name"というキーにそれぞれマッピングされます。

JSONタグのオプション一覧

GolangのJSONタグには、フィールドのエンコードやデコードに影響を与えるいくつかのオプションがあります。以下は代表的なオプションの一覧です。

1. omitempty

omitemptyは、フィールドが「ゼロ値」である場合に、そのフィールドをJSON出力から除外します。ゼロ値は、型ごとに異なります。例えば、整数型は0、文字列型は""(空文字)、スライスはnilがゼロ値に該当します。

type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name,omitempty"`
}

省略されるゼロ値

  • 数値型:0
  • 文字列型:""(空文字)
  • ポインタ型:nil
  • スライス、マップ、チャネル、インターフェース型:nil
  • ブール型:false

このオプションは、データが必要でないときに出力されるJSONをコンパクトに保つのに役立ちます。

例: フィールドの値がゼロ値のとき

{
    "id": 1
}

2. - (ハイフン)

JSONタグに"-"を指定すると、そのフィールドはエンコードおよびデコードの対象外になります。つまり、JSONに出力されません。

type User struct {
    ID    int    `json:"id"`
    Name  string `json:"-"`
}

例: "-"を使用

{
    "id": 1
}

この場合、NameフィールドはJSON出力には含まれません。

3. string

stringオプションを付けると、数値やブール値などの非文字列型のフィールドを、文字列としてエンコード・デコードします。例えば、整数型の値をJSONでは文字列として表現する場合に使います。

type User struct {
    ID int `json:"id,string"`
}

例: 数値を文字列として出力

{
    "id": "1"
}

数値型のIDが文字列としてエンコードされています。

4. omitempty,string の組み合わせ

omitemptystringを組み合わせることも可能です。この場合、ゼロ値のときはフィールドを出力せず、ゼロ値でないときは数値などを文字列として出力します。

type User struct {
    Age int `json:"age,omitempty,string"`
}

5. omitempty,- の組み合わせ

特殊なケースですが、omitempty,-のように組み合わせることもできます。これはあまり使用されませんが、ゼロ値でない場合も、そのフィールドが出力されないことを意味します。

type User struct {
    Age int `json:"age,omitempty,-"`
}

この場合、Ageは常に出力されません。

6. カスタムタグ名

JSONタグの"キー名"部分を省略すると、フィールド名がそのままキー名として使われます。以下の例ではjson:"-"で非表示にするフィールドと、omitemptyで省略されるフィールドの例を示します。

type User struct {
    ID       int    `json:"id"`
    Username string `json:"name,omitempty"`
    Password string `json:"-"`
}

例: 出力結果

{
    "id": 1,
    "name": "John"
}

この場合、Passwordフィールドは出力されません。

オプションのまとめ

オプション 説明
omitempty フィールドがゼロ値の場合、そのフィールドをJSON出力から除外する
- フィールドをエンコードおよびデコードの対象から除外
string 数値やブール値を文字列としてエンコード・デコードする
omitempty,string ゼロ値の時はフィールドを省略し、値がある場合は文字列としてエンコードする
omitempty,- ゼロ値であっても常にフィールドを出力しない(特殊なケース)
カスタムキー名 指定された名前をJSONのキー名にする

Golangでのエンコードとデコード

Golangでは、標準ライブラリのencoding/jsonパッケージを使って、構造体やマップなどのデータをJSON形式にエンコードしたり、JSONデータをデコードして構造体にマッピングすることができます。

JSONエンコード

構造体をJSONにエンコードする方法

Golangでは、構造体をJSON形式に変換するためにjson.Marshal()を使用します。次の例では、User構造体をエンコードしています。

package main

import (
	"encoding/json"
	"fmt"
)

type User struct {
	ID    int    `json:"id"`
	Name  string `json:"name"`
	Email string `json:"email_address,omitempty"`
}

func main() {
	user := User{
		ID:    1,
		Name:  "John Doe",
		Email: "john.doe@example.com",
	}

	jsonData, err := json.Marshal(user)
	if err != nil {
		fmt.Println("エンコードエラー:", err)
		return
	}

	fmt.Println(string(jsonData))
}

エンコード結果

{
    "id": 1,
    "name": "John Doe",
    "email_address": "john.doe@example.com"
}

json.Marshal()は、構造体をJSON文字列にエンコードし、[]byte型を返します。[]bytestring型に変換して表示しています。

エンコードでのポイント

  • フィールド名が大文字で始まるもの(エクスポート可能なフィールド)だけがエンコードされます。
  • omitemptyオプションを使用すると、ゼロ値のフィールドはJSON出力に含まれません。

フォーマットされたJSONを出力する方法

json.MarshalIndent()を使用すると、フォーマットされた(インデントされた)JSONを出力することができます。

jsonData, _ := json.MarshalIndent(user, "", "  ")
fmt.Println(string(jsonData))

このようにすると、次のように整形されたJSONが出力されます。

{
  "id": 1,
  "name": "John Doe",
  "email_address": "john.doe@example.com"
}

JSONデコード

JSONを構造体にデコードする方法

JSONデータをGoの構造体に変換するには、json.Unmarshal()を使用します。次の例では、JSON文字列をUser構造体にデコードしています。

package main

import (
	"encoding/json"
	"fmt"
)

type User struct {
	ID    int    `json:"id"`
	Name  string `json:"name"`
	Email string `json:"email_address"`
}

func main() {
	jsonStr := `{"id":1,"name":"John Doe","email_address":"john.doe@example.com"}`

	var user User
	err := json.Unmarshal([]byte(jsonStr), &user)
	if err != nil {
		fmt.Println("デコードエラー:", err)
		return
	}

	fmt.Printf("ID: %d, Name: %s, Email: %s\n", user.ID, user.Name, user.Email)
}

デコード結果

ID: 1, Name: John Doe, Email: john.doe@example.com

デコードのポイント

  • json.Unmarshal()では、JSONデータを構造体やマップにデコードできます。構造体をデコードする場合、ポインタを渡す必要があります(&userのように)。
  • JSONのキーが構造体フィールドに存在しない場合、そのフィールドは初期値(ゼロ値)になります。
  • JSONのフィールド名と構造体フィールド名はタグを使ってマッピングされます。タグがない場合は、デフォルトでフィールド名が使われます。

マップを使ったエンコード・デコード

マップをJSONにエンコードする

構造体ではなく、マップもJSONにエンコードできます。例えば、次のようなマップをエンコードします。

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	data := map[string]interface{}{
		"id":    1,
		"name":  "John Doe",
		"email": "john.doe@example.com",
	}

	jsonData, err := json.Marshal(data)
	if err != nil {
		fmt.Println("エンコードエラー:", err)
		return
	}

	fmt.Println(string(jsonData))
}

マップのエンコード結果

{
    "id": 1,
    "name": "John Doe",
    "email": "john.doe@example.com"
}

JSONをマップにデコードする

次に、JSON文字列をマップにデコードします。

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	jsonStr := `{"id":1,"name":"John Doe","email":"john.doe@example.com"}`

	var data map[string]interface{}
	err := json.Unmarshal([]byte(jsonStr), &data)
	if err != nil {
		fmt.Println("デコードエラー:", err)
		return
	}

	fmt.Println(data)
}

デコード結果

map[id:1 name:John Doe email:john.doe@example.com]

構造体とマップの比較

構造体のメリット

  • 型が決まっているため、データの扱いが安全で、予測可能。
  • タグを使ってJSONキー名やオプションを指定できる。

マップのメリット

  • 柔軟性が高く、任意のキー・値のペアを扱える。
  • フィールドが決まっていないJSONデータを扱いやすい。

エンコードとデコードのエラーハンドリング

エンコードやデコード時にはエラーが発生する可能性があるため、エラーハンドリングは重要です。次のように、errをチェックしてエラー処理を行います。

jsonData, err := json.Marshal(user)
if err != nil {
    fmt.Println("エンコードエラー:", err)
}

err := json.Unmarshal([]byte(jsonStr), &user)
if err != nil {
    fmt.Println("デコードエラー:", err)
}

まとめ

Golangでは、json.Marshal()json.Unmarshal()を使用して簡単に構造体やマップをJSONにエンコード・デコードすることができます。JSONタグを活用して、キー名のカスタマイズや省略処理も可能です。エンコードとデコードの方法を理解することで、効率的なデータ操作が可能になります。

参考文献:

Discussion