JSONからGo構造体変換
JSONからGoの構造体を自動生成
JSON入力
Go出力
JSONからGoの構造体への変換とは?
JSONからGoの構造体への変換は、生のJSONデータをGoの標準ライブラリにあるencoding/jsonパッケージで動作する型定義に変換します。Goは静的型付け言語のため、すべてのJSONフィールドに対して正しい型と、シリアライザーにマッピング先のJSONキーを伝えるstructタグを持つフィールドが必要です。大規模または深くネストされたAPIレスポンスに対してこれらの定義を手書きするのは時間がかかり、ミスが起きやすい作業です。
Go 1.0から仕様で定義されているencoding/jsonパッケージは、リフレクションを使用してJSONキーをエクスポートされた構造体フィールドにマッピングします。デフォルトではマッチングは大文字小文字を区別しませんが、明示的なjson structタグが標準的な実装方法です。これにより曖昧さがなくなり、GoのPascalCase規約を使いながらJSONをcamelCaseやsnake_caseのままにできます。変換ツールはこの作業を自動化します:JSONを読み込み、値からGoの型を推論し、正しいタグを持つ構造体定義を出力します。
Goの型システムはJSONに自然にマッピングできます。文字列はstring、真偽値はbool、整数はint、浮動小数点数はfloat64になります。ネストされたJSONオブジェクトは別々の名前付き構造体になり、配列はスライスになります。唯一のギャップはnullです:Goには汎用のnullable型がないため、null値は通常ポインター型(*string、*int)またはinterface{} になります。生成ツールはこれらすべてをミリ秒で処理します。
JSONからGoの変換ツールを使う理由
JSONからGoの構造体を手書きするということは、波かっこを数え、型を推測し、APIが変わるたびにタグを書き直すことを意味します。変換ツールはその手間を省きます。
JSONからGoの構造体への変換ユースケース
JSONからGoへの型マッピング
encoding/jsonパッケージはJSONの値をGoの型にマッピングする際に特定のルールに従います。下表はデフォルトのマッピングと一般的な代替型を示しています。「デフォルト」列はほとんどの生成ツールが出力する型です。「代替」列は大きなIDにint64、nullableフィールドにポインター型を使うなど、要件に応じて選択する型を示しています。
| JSON型 | 値の例 | デフォルト | 代替 |
|---|---|---|---|
| string | "hello" | string | string |
| number (integer) | 42 | int | int64 |
| number (float) | 3.14 | float64 | float64 |
| boolean | true | bool | bool |
| null | null | interface{} | *string / *int |
| object | {"k": "v"} | struct | struct |
| array of strings | ["a", "b"] | []string | []string |
| array of objects | [{"id": 1}] | []Item | []Item |
| mixed array | [1, "a"] | []interface{} | []interface{} |
Go JSONのstructタグリファレンス
structタグはencoding/jsonがフィールドをシリアライズ・デシリアライズする方法を制御します。jsonタグが圧倒的に一般的ですが、同じフィールドに他のタグ(db、yaml、xml)と組み合わせることもできます。タグの構文はフィールド型の後ろにバッククォートで囲まれた文字列です。以下はencoding/jsonがサポートするjsonタグのオプションです。
| タグ | 動作 | 使用場面 |
|---|---|---|
| json:"name" | Maps struct field to JSON key "name" | Always generated |
| json:"name,omitempty" | Omits field from output if zero value | Optional fields |
| json:"-" | Field is never serialized or deserialized | Internal fields |
| json:"name,string" | Encodes int/bool as JSON string | String-encoded numbers |
json.Unmarshal と json.NewDecoder の違い
GoはJSONのデコードに2つの方法を提供しています:バイトスライス用のjson.Unmarshalとio.Readerストリーム用のjson.NewDecoderです。どちらも同じstructタグのルールを使用しますが、使いどころが異なります。
コード例
以下の例は、JSONから生成したGoの構造体を実際のプログラムで使用する方法と、他の言語から生成する方法を示しています。Goの例は標準ライブラリのencoding/jsonを使用しています。
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
IsActive bool `json:"is_active"`
Tags []string `json:"tags"`
}
func main() {
data := []byte(`{"id":1,"name":"Alice","email":"alice@example.com","is_active":true,"tags":["admin","editor"]}`)
var user User
if err := json.Unmarshal(data, &user); err != nil {
panic(err)
}
fmt.Println(user.Name) // → Alice
fmt.Println(user.Tags) // → [admin editor]
}// JSON response from API — this is the shape you'd convert to Go:
const res = await fetch("https://api.example.com/users/1");
const user = await res.json();
// user → { "id": 1, "name": "Alice", "email": "alice@example.com" }
// Equivalent Go struct:
// type User struct {
// ID int `json:"id"`
// Name string `json:"name"`
// Email string `json:"email"`
// }import json
def json_to_go(data: dict, name: str = "Root") -> str:
lines = [f"type {name} struct {{"]
type_map = {str: "string", int: "int", float: "float64", bool: "bool"}
for key, value in data.items():
go_type = type_map.get(type(value), "interface{}")
field = key.title().replace("_", "") # snake_case → PascalCase
lines.append(f'\t{field} {go_type} `json:"{key}"`')
lines.append("}")
return "\n".join(lines)
data = json.loads('{"user_name": "Alice", "age": 30, "verified": true}')
print(json_to_go(data))
# type Root struct {
# UserName string `json:"user_name"`
# Age int `json:"age"`
# Verified bool `json:"verified"`
# }package main
import (
"encoding/json"
"fmt"
"strings"
)
type Event struct {
Type string `json:"type"`
Payload string `json:"payload"`
}
func main() {
stream := strings.NewReader(`{"type":"click","payload":"btn-1"}
{"type":"scroll","payload":"page-2"}`)
dec := json.NewDecoder(stream)
for dec.More() {
var ev Event
if err := dec.Decode(&ev); err != nil {
break
}
fmt.Printf("%s: %s\n", ev.Type, ev.Payload)
}
// → click: btn-1
// → scroll: page-2
}