Menu
Coddy logo textTech

encoding/json と構造体

CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 76/107。

encoding/jsonパッケージを使用すると、Goの構造体をJSONに変換したり、その逆に変換したりできます。これは、API、設定ファイル、およびデータストレージにとって不可欠です。構造体タグは、フィールドがJSONキーにどのようにマッピングされるかを制御します。

構造体をJSONに変換するには、json.Marshalを使用します:

type User struct {
    Name  string `json:"name"`
    Email string `json:"email"`
    Age   int    `json:"age"`
}

func main() {
    user := User{Name: "Alice", Email: "alice@example.com", Age: 30}
    data, _ := json.Marshal(user)
    fmt.Println(string(data))
    // {"name":"Alice","email":"alice@example.com","age":30}
}

`json:"name"` のような構造体タグは、JSONのキー名を指定します。タグがない場合、Goはフィールド名をそのまま使用します。エクスポートされたフィールド(大文字で始まるもの)のみがJSON出力に含まれます。

JSONを構造体に戻すには、json.Unmarshalを使用します:

jsonStr := `{"name":"Bob","email":"bob@example.com","age":25}`
var user User
json.Unmarshal([]byte(jsonStr), &user)
fmt.Println(user.Name)  // Bob

一般的なタグのオプションには、ゼロ値をスキップするための omitempty や、フィールドを完全に無視するための - が含まれます:

type Product struct {
    ID       int    `json:"id"`
    Name     string `json:"name"`
    Price    float64 `json:"price,omitempty"`
    Internal string `json:"-"`
}

omitempty を使用すると、Price がゼロの場合、JSON には表示されません。Internal フィールドは、その値に関係なく、決して含まれません。

challenge icon

チャレンジ

簡単

Goの構造体とJSONを相互に変換する映画カタログシステムを構築しましょう!構造体をJSONにマーシャリングし、JSONを構造体にアンマーシャリングする練習をします。その際、構造体タグを使用してJSONのフィールド名を制御します。

コードは2つのファイルに分けて構成します:

  • movie.go: 適切なJSONタグを使用して映画データモデルを定義します。

    以下のフィールドとJSONマッピングを持つ Movie 構造体を作成してください:

    • Title (string) は "title" にマッピング
    • Director (string) は "director" にマッピング
    • Year (int) は "year" にマッピング
    • Rating (float64) は "rating" にマッピングし、評価のない映画にratingフィールドが表示されないよう omitempty を指定
    • InternalID (string) は "-" を使用してJSONから完全に除外

    MovieをJSON文字列に変換する関数 ToJSON(m Movie) string を作成してください。また、JSON文字列をMovie構造体にパースする FromJSON(jsonStr string) Movie も作成してください。

  • main.go: 両方向のJSON変換を実演します。

    操作タイプ(encode または decode)を読み取ります。

    encode の場合:映画の詳細(タイトル、監督、公開年、評価)を別々の行で読み取ります。InternalIDを "internal-123" としたMovieを作成し、それをJSONに変換して結果を出力します。

    decode の場合:JSON文字列を読み取り、Movie構造体に変換して、映画の詳細を以下の形式で出力します:

    Title: [title]
    Director: [director]
    Year: [year]
    Rating: [rating]

    評価は小数点以下1位まで表示してください。評価が0(JSONに存在しない)の場合は、代わりに Rating: N/A と出力してください。

以下の入力が提供されます:

  • 1行目:操作タイプ(encode または decode
  • encodeの場合:タイトル、監督、公開年、評価(4行)
  • decodeの場合:JSON文字列(1行)

例えば、以下が与えられた場合:

encode
Inception
Christopher Nolan
2010
8.8

出力は以下のようになります:

{"title":"Inception","director":"Christopher Nolan","year":2010,"rating":8.8}

"-" タグのため、出力に InternalID が表示されないことに注目してください。

また、以下が与えられた場合:

encode
The Matrix
Wachowskis
1999
0

出力は以下のようになります:

{"title":"The Matrix","director":"Wachowskis","year":1999}

値がゼロで omitempty が指定されているため、rating が省略されていることに注目してください。

また、以下が与えられた場合:

decode
{"title":"Interstellar","director":"Christopher Nolan","year":2014,"rating":8.6}

出力は以下のようになります:

Title: Interstellar
Director: Christopher Nolan
Year: 2014
Rating: 8.6

また、以下が与えられた場合:

decode
{"title":"Tenet","director":"Christopher Nolan","year":2020}

出力は以下のようになります:

Title: Tenet
Director: Christopher Nolan
Year: 2020
Rating: N/A

チートシート

encoding/json パッケージは、json.Marshaljson.Unmarshal を使用して Go の構造体を JSON に、またはその逆に変換します。

構造体を JSON に変換するには:

type User struct {
    Name  string `json:"name"`
    Email string `json:"email"`
    Age   int    `json:"age"`
}

user := User{Name: "Alice", Email: "alice@example.com", Age: 30}
data, _ := json.Marshal(user)
fmt.Println(string(data))
// {"name":"Alice","email":"alice@example.com","age":30}

JSON を構造体に戻すには:

jsonStr := `{"name":"Bob","email":"bob@example.com","age":25}`
var user User
json.Unmarshal([]byte(jsonStr), &user)
fmt.Println(user.Name)  // Bob

構造体タグは JSON フィールドのマッピングを制御します:

  • `json:"name"` - フィールドを JSON キー "name" にマッピングします
  • `json:"price,omitempty"` - 値がゼロ値の場合、フィールドを省略します
  • `json:"-"` - フィールドを JSON から完全に除外します
type Product struct {
    ID       int     `json:"id"`
    Name     string  `json:"name"`
    Price    float64 `json:"price,omitempty"`
    Internal string  `json:"-"`
}

エクスポートされたフィールド(大文字で始まるもの)のみが JSON 出力に含まれます。

自分で試してみよう

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// 操作タイプを読み込む
	operation, _ := reader.ReadString('\n')
	operation = strings.TrimSpace(operation)

	if operation == "encode" {
		// 映画の詳細を読み込む
		title, _ := reader.ReadString('\n')
		title = strings.TrimSpace(title)

		director, _ := reader.ReadString('\n')
		director = strings.TrimSpace(director)

		yearStr, _ := reader.ReadString('\n')
		yearStr = strings.TrimSpace(yearStr)
		year, _ := strconv.Atoi(yearStr)

		ratingStr, _ := reader.ReadString('\n')
		ratingStr = strings.TrimSpace(ratingStr)
		rating, _ := strconv.ParseFloat(ratingStr, 64)

		// TODO: 読み込んだ値を使用して Movie 構造体を作成する
		// InternalID を "internal-123" に設定する
		// ToJSON を使用して JSON に変換し、結果を出力する

	} else if operation == "decode" {
		// JSON 文字列を読み込む
		jsonStr, _ := reader.ReadString('\n')
		jsonStr = strings.TrimSpace(jsonStr)

		// TODO: FromJSON を使用して JSON を Movie に変換する
		// 必要な形式で映画の詳細を出力する
		// 評価のフォーマットには fmt.Sprintf("%.1f", rating) を使用する
		// rating が 0 の場合は、代わりに "Rating: N/A" を出力する

	}
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン