JSON → Go 変換

JSON から json タグ付きの Go 構造体を生成。数値は書き方で型を決め、欠けうる値はポインタに。すべてブラウザ内で動作します。

入力
Go
type Root struct {
	Orders  []Order `json:"orders"`
	HasMore bool    `json:"has_more"`
}

type Order struct {
	ID        int64    `json:"id"`
	UserID    int64    `json:"user_id"`
	CreatedAt string   `json:"created_at"`
	Price     float64  `json:"price"`
	Coupon    *string  `json:"coupon"`
	GiftNote  *string  `json:"gift_note,omitzero"`
	Shipping  Shipping `json:"shipping"`
	Tags      []any    `json:"tags"`
}

type Shipping struct {
	City     string          `json:"city"`
	Postcode json.RawMessage `json:"postcode"`
}
  • 以下の各位置では、どの数値も整数として読み取られたため、型は int64 です。小数点や指数を含む値は、10.0 であってもこの型にデコードされません。

    位置: 2

    • Order.ID
    • Order.UserID
  • 以下の各位置では、値の種類が一つではありません(たとえば数値と文字列)。そのため型は json.RawMessage で、各値を JSON に書かれたとおりに保持するので、どの種類かがわかった時点でプログラム側でデコードします。種類の一つがオブジェクトの場合も、その構造体は出力に残っており、その値をそこにデコードできます。

    位置: 1

    • Shipping.Postcode
  • 以下の各位置では、型を推論する材料がありませんでした。そこに現れたのが null だけだった、配列が常に空だった、オブジェクトにキーがなかった、または値がこのツールでたどれないほど深く入れ子になっていた、のいずれかです。そのため型は any で、どんな値も受け付けます。オブジェクトにキーがなかった箇所は map[string]any で、どんなオブジェクトも null も受け付けますが、それ以外は受け付けません。

    位置: 1

    • Order.Tags[]

元になった JSON をデコードする構造体

JSON のサンプルを貼り付けると、このページがそのサンプルに対する Go の宣言を書き出します。サンプルの中のオブジェクトごとに名前付きの構造体を一つずつ作り、すべてのフィールドには、そのフィールドが読み取るキーを示す json タグを付けます。以下のことはすべて、一つの約束で決まります。Go 1.27 でビルドしたプログラムに入れると、宣言は貼り付けた JSON を "encoding/json" でエラーなくデコードし、どのキーもフィールドに読み込まれます。例外は、どの構造体タグでも表せないキーで、そうしたキーは構造体から外されます。Go がこの約束を守らせてくれないところや、データが決めていないことをページに選ばせるところでは、出力の下の知らせがその場所を示します。まれな例外が一つあり、それはタグの節で説明します。

形そのものは、Go を書き始める前に、「JSON → TypeScript 変換」のページが使っているのと同じ JSON の読み取りによって割り出されます。配列の要素をマージすること、一部の要素にしかないキーに気づくこと、null と一度も送られなかったキーを区別すること、別のオブジェクトの中で見つかったオブジェクトすべてに名前を付けること。そのすべてはそのページのガイドが扱っているので、以下では繰り返しません。この先で扱うのは Go の受け持ちの部分、つまり数値ごとの型、ポインタ、値の種類が異なるキーのための型、フィールド名、タグ、そして知らせが投げかける問いです。

数値ごとの書き方で選ぶ、三つの数値型

JSON を読み取るブラウザは、10 と 10.0 を同じ数値にします。Go はそうしません。"int64" のフィールドは、小数点や指数を付けて書かれた数値 — 10.0、1e3、さらには -0.0 — を受け付けず、11 と -0 は受け付けます。そのためページは、値だけから数値型を選ぶことはしません。ブラウザ自身のパーサーに各数値がどう書かれていたかを尋ね、それをもとに次のように決めます。

  • ある位置のどの数値も、小数点も指数もない整数として書かれているところでは、フィールドは "int64" になり、整数についての知らせがそのフィールドを挙げます。
  • それ以外の数値は "float64" です。ですから 10.0 と書かれた価格は、サンプルのどの価格にも端数がなくても "float64" になります。Python の "json" モジュールは値が整数の float をまさにそのように書くので、これにふつう出会うのは Python で提供される API です。
  • "json.Number" は、どちらの型も正確に保持できない数値のためにとってあります。"int64" の範囲をどちらかの端で超える整数、1e999 のように "float64" にはそもそも大きすぎる数値、そして [9007199254740993, 1.5] のように小数と並んでいて "float64" には長すぎる整数です。この型はそのどれも正確に保持し、値を使うところであなたのプログラムが変換します。

ほかの整数の中にある長い整数には、そうした配慮は要りません。[9007199254740993, 1] は "[]int64" です。"float64" が保持できるより多くの桁を持つ小数は "float64" のまま残され、専用の知らせで挙げられます。デコードして再びエンコードすると、0.30000000000000001 は 0.3 になって戻ってくるからです。

数値がどう書かれたかを読み取るには、それを報告するブラウザが必要です。報告しないブラウザは各数値を値だけから読み取るので、そこでは 10.0 が整数に見え、そのフィールドは "int64" になって、その JSON をデコードできません。ページは整数と区別できなかった数値を数え、最初のものがどこにあるかを示します。そうしたブラウザには "float64" が落とす桁も見えないので、そこでは小数と並ぶ長い整数が "float64" と型付けされ、丸められる小数は挙げられません。

値が欠けうるところにはポインタ

一部のオブジェクトが省くキーや、null にするキーはポインタになります。"*string"、"*int64"、あるいはオブジェクトのために書かれた構造体へのポインタです。null を含む配列の要素もポインタになるので、[1, null] は "[]*int64" です。一部のオブジェクトで省かれるキーには、さらにタグに "omitzero" が付きます。この二つがそろうと、フィールドは値が届いたかどうかを表せます。そしてデコードした構造体を再びエンコードすると、サンプルで省かれるだけだったキーは再び省かれ、null にされるだけだったキーは null のまま書き戻されます。

スライス、マップ、"any"、"json.RawMessage" はポインタになりません。何もデコードされなければ、どれもはじめから nil だからです。それでもスライスには、そのキーが欠けうるところで "omitzero" が付き、存在していたが空だった配列は [] として書き戻されます。

残らない区別が一つあります。一部のオブジェクトにはなく、別のオブジェクトでは null になっているキーは、Go では一つの nil なので、プログラムは二つを見分けられません。構造体を再びエンコードすると、サンプルに null があったところでもそのキーは省かれます。こうなるフィールドは、ページがすべて挙げます。値の種類が混在するキーは違いを保ちます。"json.RawMessage" は null を null というバイト列として保持するからです。

種類の混在には json.RawMessage、何も見られなかったところには any

キーの値の種類が食い違うとき — ある場所では数値、別の場所では文字列やオブジェクト — フィールドは "json.RawMessage" になります。これは値そのもののバイト列で、どの種類が届いたかを確かめてからプログラムがデコードできるよう残されます。その中に null があっても何も変わりません。種類の一つがオブジェクトなら、そのために抽出された構造体も、ほかの構造体と並んで出力されます。"any" もどの種類でも受け付けますが、混在には使いません。"any" にデコードされた数値は "float64" になり、"float64" が保持できないものを失うからです。

サンプルには、学べる値がまったくない部分があります。null しか現れなかったキー、毎回空だった配列、キーのないオブジェクト、そして推論がたどれるよりも深く入れ子になった値です。Go では、null のキーと入れ子の深い値に "any"、空の配列の要素に "[]any"、オブジェクトに "map[string]any" が当てられます。"any" はどんな種類の値も受け付けます。マップが受け付けるのはオブジェクトか null で、その場所に文字列、数値、配列、真偽値があるとデコードに失敗します。

その深さを超えたところでは、知らせが語らないことが二つあります。そこで一部のオブジェクトにはなく、別のオブジェクトでは null になっているキーは、型を推論する材料のない位置として挙げられるだけで、Go はその null を省いて書き戻します。また、"float64" が保持できるより長い整数は丸められて戻り、9007199254740993 は 9007199254740992 として返ってきます。その位置が "any" だからです。

フィールド名は Go の流儀で、構造体名は TypeScript のページと共通

フィールドの名前は、そのキーを Go 流に綴ったものです。キーは、文字でも数字でもないもののところと、小文字の後に大文字が続くところで単語に分けられ、単語はそれぞれ大文字で始めてつなげられます。ですから "user_name"、"last-name"、"firstName" は UserName、LastName、FirstName になります。staticcheck の既定の頭字語リストにある単語は大文字で書かれ、"id" は ID、"api_key" は APIKey、"video_url" は VideoURL になります。ただし数に入るのは単語まるごとだけなので、"idle" は Idle、複数形の "ids" は Ids です。すべて大文字で書かれたキーは単語として読まれ、"USER_ID" は UserID になります。

キー自身の文字はそのまま残るので、"имя" は Имя になります。名前が大文字で始まらない場合、つまり "名前" のように大文字のない文字体系のキー、"1st" のように数字で始まるキー、"ß" のように一文字の大文字形を持たない文字で始まるキーでは、先頭に X が付いて X名前、X1st、Xß となり、それでもフィールドは自分のキーを読み取ります。結合文字は名前から除かれ、タグには残ります。"@" のように文字も数字もまったく含まないキーは Field と名付けられ、同じ構造体の中ですでに使われている名前には番号が付きます。"user_id"、"userId"、"USER_ID" がそろうと、UserID、UserID2、UserID3 になります。

Go の流儀が及ぶのはフィールド名までです。どの構造体も推論がそのオブジェクトに選んだ名前を保つので、ここの宣言と TypeScript のページのインターフェースは同じ名前になり、フィールドとそれが保持する構造体とで綴りが異なることもあります。その名前の選び方は TypeScript のガイドで説明しています。

Go 1.27 向けのタグと、どのタグでも表せないキー

各フィールドのタグは、json:"user_id" のようにキーをそのまま示し、キーが欠けうるところではキーの後に ,omitzero が付きます。キーの中の制御文字は Go 自身が使うエスケープで書かれ、キー "-" は json:"-," と書かれます。Go がそのキーとして読み取る形です。

タグは Go 1.27 向けに書かれています。Go 1.26 がタグを通して読み取れるキーはもっと少なく、文字、数字、ASCII の空白、一部の ASCII の句読点以外のものを含むキー — "Price (€)"、"temp °C"、絵文字、制御文字、結合文字 — はそこでは読み取られず、ページはそうしたフィールドを一つずつ挙げます。この半分は、"GOEXPERIMENT=nojsonv2" でビルドした Go 1.27 に対して確かめています。このビルドはタグを Go 1.26 と同じように読みますが、Unicode のテーブルは Go 1.27 の新しいものなので、Go 1.26 の古いテーブルにない文字を含むキーはこの確認では読み取られ、知らせも出ません。

カンマ、バックスラッシュ、二重引用符、アポストロフィ、バッククォートのいずれかを含むキーと空のキーは、どの構造体タグでも表せないので、フィールドをまったく持ちません。それでも JSON はデコードできます。"json.Unmarshal" はそのキーを黙って読み飛ばします。ただし "DisallowUnknownFields" を設定した "json.Decoder" はそこで止まります。知らせはそうしたキーを、その構造体と、Go が文字列を引用するとおりに引用したキーとで、Order["note,internal"] のように挙げます。そうしたキーの下にあるオブジェクトにも、構造体は出力されます。知らせの出ない場合が一つあります。どの文字も表さないエスケープである、対になっていないサロゲートだけが異なる二つのキーは、Go にとっては一つのキーで、どちらのフィールドも埋まりません。

宣言だけを、gofmt のレイアウトで

出力に含まれるのは型宣言だけです。package 句も import もないので、すでにあるファイルの、そのファイル自身の package 句の下に入れられます。"json.RawMessage" や "json.Number" を使うところでは、ファイルに "encoding/json" の import も必要になり、それがあなたかエディターに任される唯一の行です。レイアウトは gofmt そのもので、各フィールドの前にタブを置き、各構造体の名前、型、タグを列にそろえるので、gofmt はそれをまったく変えません。ルートの型が最初に出力され、どのオブジェクトも、それを保持するフィールドの中にじかに書かれた構造体ではなく名前付きの型になります。トップレベルが配列や単一の値である JSON も、"type Root []RootItem" のように名前付きの型になるので、デコード先の型が必ずあります。

知らせが確かめるよう求めていること

Go が強いた判断の種類ごとに、宣言の下に知らせが一つ出て、その知らせは当てはまるすべての位置をまとめます。ですから整数のフィールドがたくさんあるサンプルでも、知らせはフィールドごとではなく一つだけです。位置は宣言が綴るとおりに綴られます。Order.UserID はフィールド、Order.Tags[] は配列の要素で、ルートはその名前だけです。ただし二つの知らせは、代わりに行と列で JSON の中を指します。二度書かれたキーと、このブラウザが整数と区別できなかった数値です。どの知らせも宣言を変えません。それぞれを、データについての問いとして読んでください。

  • "int64" と型付けされた整数。そこでは、どの数値も整数として書かれていました。後から届く値に、価格や測定値のように小数点や指数が付くことがあるなら、その値はデコードされないので、そのフィールドを "float64" にしてください。ID や個数なら、そのままでかまいません。
  • "json.Number"。ほかのどの数値型も、それらの値を正確には保持できません。プログラムが使うところでそれぞれを変換するか、実際の値がもっと狭い型に収まると分かっているなら、フィールドを自分で変更してください。
  • "float64" が丸める小数。そこには "float64" が保持できるより多くの桁を持つ数値があるので、プログラムが受け取るのは書かれた値ではなく、その近くの値です。すべての桁が重要なところでは、フィールドを "json.Number" にしてください。
  • "json.RawMessage" として保たれた種類の混在。届いた値をそれぞれ確かめ、実際にそうだった種類としてデコードしてください。種類の一つがオブジェクトなら、その構造体は出力に残っているので、その値をそこにデコードできます。
  • 型を推論する材料がない。サンプルのそこには学べる値がなかったので、フィールドは "any" か、"any" のマップです。そのフィールドが持つと分かっている型に置き換えるか、実際の値が入った JSON から変換し直してください。
  • 一部のオブジェクトにはなく、別のオブジェクトでは null。Go は両方に一つの nil しか持たないので、キーは省かれて書き戻されます。これが問題になるのは、出力を読む側が null を、存在しないキーとは違うものとして扱う場合だけです。
  • Go 1.26 が読み取らないキー。Go 1.27 でビルドするか、キーが作られるところでそれらの名前を変えてください。Go 1.26 では読み取られないままです。
  • 外されたキー。そうしたキーにはフィールドがありません。読み取るには、知らせ自身が勧めるとおり、オブジェクトを "map[string]json.RawMessage" にデコードするか、構造体に "UnmarshalJSON" メソッドを書いてください。
  • 二度書かれたキー。型はキーの最後のものに従いますが、Go はそれぞれを順にデコードするので、型が保持できない前のもの(たとえば最後のものが数値なのに文字列)があるとデコードはエラーを返し、オブジェクトの前のものにしかないキーは読み取られません。示された行と列で JSON を直してください。
  • このブラウザは 10 と 10.0 を区別できません。数値がどう書かれているかを報告しないので、小数点や指数を付けて書かれた値から "int64" と型付けされたフィールドは、JSON をデコードできません。値がそのように書かれたフィールドを確かめるか、それを報告するブラウザで JSON を変換してください。

日付や形式、決まった値の集合についての知らせはありません。ページが文字列からそれを推測することは決してないからです。例の "created_at" は、どう見えようと "string" のままです。

よくある質問

私の JSON の価格はどれも端数のない数なのに、価格が float64 になるのはなぜですか?
どの価格も 10.0 のように小数点付きで書かれていて、"int64" のフィールドは、端数があってもなくても、そう書かれた数値を受け付けないからです。ページは値だけでなく各数値の書き方を読むので、フィールドは "float64" になり、JSON はデコードできます。値だけから "int64" と型付けしていたら、デコードは最初の価格で止まっていたでしょう。
数値が json.Number になるのはどんなときですか?
その値のどれかが、"int64" の範囲を超える整数、"float64" には大きすぎる数値、または小数と並んでいて "float64" なら丸めてしまう長い整数であるときです。"json.Number" はそのどれも正確に保ち、出力の下の知らせが、それが選ばれたフィールドをすべて挙げます。
一部のフィールドがポインタなのはなぜで、タグの omitzero は何をしているのですか?
キーが一部のオブジェクトで欠けているか、一部で null になっているところでは、フィールドはポインタになり、nil で値が届かなかったことを表せます。"omitzero" はキーが一部のオブジェクトで欠けているところに付きます。すると構造体を再びエンコードしたとき、そのキーは null と書かれる代わりに、JSON がそうしていたとおり省かれます。
種類が混在するキーが、any ではなく json.RawMessage なのはなぜですか?
"json.RawMessage" は、プログラムが読み方を決めるまで各値のバイト列を保っておくからです。"any" もそれらの値を受け付けはしますが、"any" にデコードされた数値は "float64" になり、長い整数はそこで末尾の桁を失います。
一部のフィールド名が X で始まるのはなぜですか?
そうしないと名前が大文字で始まらないからです。キーが大文字のない文字体系で書かれているか、数字で始まるか、一文字の大文字形を持たない文字で始まっています。X のおかげでキー自身の文字が名前に残り、フィールドはそれでも自分のキーを読み取ります。X名前 は "名前" を読み取ります。
構造体に必要な Go のバージョンはどれですか?
Go 1.27 向けに書かれ、Go 1.27 で確かめられています。Go 1.26 がタグをどう読むかも、代役を立てて確かめています。Go 1.26 は、文字、数字、ASCII の空白、一部の ASCII の句読点以外の文字、たとえば通貨記号や絵文字を含むキーを読み取らず、ページはそれが影響するキーを持つフィールドをすべて挙げます。ただし、Go 1.26 の Unicode のテーブルより新しい文字を含むキーは別で、ページにもその確認にも見えません。
私のキーの一つが構造体にないのはなぜですか?
そのキーがカンマ、バックスラッシュ、二重引用符、アポストロフィ、バッククォートのいずれかを含むか、空であり、そうしたキーはどの構造体タグでも表せないからです。知らせは Root["a,b"] のように、その構造体とキーそのもので挙げます。JSON の残りはそのままデコードでき、知らせはそのキーを別の方法で読み取る手順も伝えます。
トークンやパスワードを含むレスポンスを貼り付けても安全ですか?
はい。すべての処理はあなたのコンピューター上のこのページの中で行われ、貼り付けたレスポンスはほかのどこにも行きません。受け取るサーバーはなく、写しを残すものもありません。そこから作られる宣言が持つのはフィールド名、タグ、Go の型だけなので、JSON の中のトークンやパスワードは、そのキーにちなんで名付けられた "string" のフィールドのほかには、宣言に何の跡も残しません。

関連するツール

  • JSON → TypeScript 変換

    キーの値の種類が一つではないところでは、Go に union がないため、このページは各値を JSON に書かれたとおりに保持し、プログラム側でデコードするよう残します。あちらのページは同じ形を同じ型名の TypeScript の型として書き出し、そこを union にします。その形の割り出し方は、あちらのガイドが説明しています。

  • JSON → Zod 変換

    Go には単なる JSON の数値にあたる型がないため、どの数値も整数として書かれているところでは、このページはフィールドを整数型にし、小数点を付けて書かれた値はそこにデコードされません。あちらのページは同じ形を同じ型名の Zod スキーマとして書き出します。スキーマはデータが届くたびにそれを検査し、そうしたフィールドでは小数も受け付けます。

  • JSONPath テスター

    RFC 9535 の JSONPath クエリを JSON に対してテスト。

  • Markdown テーブル生成

    CSV・TSV・JSON から Markdown テーブルを生成して整形します。