JSON to Go Struct Generator

Generate Go struct definitions from JSON with json tags, nested structs, slices, and proper Go naming conventions.

What is JSON to Go Struct Generator?

Go is a statically typed language — every JSON field you unmarshal must map to a named field in a struct, complete with a `json:"..."` tag that matches the original key. Writing these structs by hand for a large API response is error-prone and tedious: a 20-field nested object means 20+ lines of boilerplate, PascalCase conversions, and tag alignment. This generator handles all of that automatically. Paste any JSON object — flat, deeply nested, or array-rooted — and get idiomatic Go structs in one click. It infers the correct type for every field (including time.Time for ISO-8601 strings), emits child structs before parent structs so the file compiles without forward declarations, and optionally adds omitempty or pointer types for nullable fields. Go's encoding/json package relies entirely on struct tags and field names for marshaling and unmarshaling. There is no reflection-based magic like in Python or JavaScript — if the field name or tag is wrong, the value is silently dropped. The generator prevents that silent failure class entirely.

How to Use

  1. Set the root struct name (e.g. "APIResponse" or "UserProfile") — this becomes the outermost type name in your Go file
  2. Decide whether to enable "Use pointers": turn it on when nested objects in the JSON can be absent (null) at runtime, so you can check for nil instead of getting a zero-value struct
  3. Enable "Detect dates" if your JSON contains ISO-8601 timestamp strings — they will be typed as time.Time rather than string, saving you a manual parse step
  4. Toggle "omitempty" if you need to re-serialize the struct back to JSON and want absent fields omitted rather than emitted as zero values
  5. Paste your JSON into the left panel and click "Generate Go Structs" — copy the output directly into your .go file alongside your package declaration

Why Use This Tool?

Eliminates hand-writing json tags — the most common source of silent unmarshal bugs in Go
Generates child structs first so the output file compiles without any reordering
Correctly handles Go's zero-value problem: null JSON values become interface{} or *T, not their zero equivalent
Converts any naming convention (camelCase, snake_case, kebab-case) to PascalCase while keeping the original key in the json tag
Detects ISO-8601 date strings and emits time.Time, which plugs directly into Go's standard library time formatting
Runs entirely in the browser — your API response data never leaves your machine

Tips & Best Practices

  • Use a real response from your API rather than a hand-crafted example — type inference is only as good as the actual values; a field that contains "123" as a string in one response may be an integer in another
  • If a field is sometimes present and sometimes absent across responses, enable "Use pointers" for nested objects and add omitempty — the generated struct will handle both cases cleanly
  • After generating, add import "time" manually if the tool emits time.Time fields and your file does not already import it; the generator outputs the struct body only
  • For large arrays in JSON responses, the generator infers types from the first element only — inspect the output if your arrays contain heterogeneous types
  • Go 1.18+ generics are not used in generated code by design: the output is compatible with all Go versions that support encoding/json

Frequently Asked Questions

What is the full JSON type → Go type mapping?

JSON string → string (or time.Time if ISO-8601 detected). JSON integer → int. JSON float → float64. JSON boolean → bool. JSON null → interface{} (or *T if the field is a nested struct and "Use pointers" is on). JSON array → []T where T is inferred from the first element. JSON object → a separate named struct. There is no direct Go equivalent of JSON's dynamic typing, so heterogeneous arrays fall back to []interface{}.

Why does Go need PascalCase field names?

In Go, only exported (capital-letter) identifiers are accessible outside a package — and encoding/json can only read and write exported struct fields. If a field starts with a lowercase letter, json.Unmarshal silently ignores it. This generator always emits PascalCase field names and preserves the original JSON key in the `json:"originalKey"` tag, so both the Go compiler and the JSON decoder are satisfied.

When should I use pointer types for nested structs?

Use pointers (*Address instead of Address) when the nested object can legitimately be null or absent in the JSON. With a value type, json.Unmarshal leaves every field at its zero value when the JSON key is null — you cannot distinguish "null" from "empty object". With a pointer type, json.Unmarshal sets the field to nil for null, which you can check explicitly. Enable "Use pointers" in the options to generate pointer types automatically.

What is omitempty and when should I use it?

omitempty is a json tag modifier that tells Go's encoder to skip the field entirely when its value is the zero value (0, false, "", nil, empty slice). Use it when you are re-serializing the struct back to JSON and want a clean output without noise fields. Do not use it for required fields or fields where the zero value has semantic meaning (e.g. a score of 0 is valid, not "absent").

When should I NOT use this generator and write structs by hand?

Skip the generator when your unmarshaling logic is non-standard: custom json.Unmarshaler implementations, fields that can be multiple types (union types), deeply polymorphic payloads, or cases where field presence itself is semantically meaningful and you need to use json.RawMessage. The generator assumes a straightforward shape-to-struct mapping; anything requiring runtime type switching is better written manually.

Is my data sent to a server?

No. All struct generation runs entirely in your browser using JavaScript. Your JSON data never leaves your device.

Real-world Examples

Parsing a GitHub REST API user response

The GitHub API returns a deeply nested user object. Manually writing the Go struct for even a partial response takes 30+ lines. Pasting the response below into the generator with "Detect dates" enabled produces a ready-to-unmarshal struct with time.Time fields for created_at and updated_at.

Input
{
  "login": "octocat",
  "id": 1,
  "node_id": "MDQ6VXNlcjE=",
  "avatar_url": "https://github.com/images/error/octocat_happy.gif",
  "public_repos": 2,
  "public_gists": 1,
  "followers": 20,
  "following": 0,
  "created_at": "2008-01-14T04:33:35Z",
  "updated_at": "2008-01-14T04:33:35Z",
  "plan": {
    "name": "pro",
    "space": 976562499,
    "private_repos": 9999,
    "collaborators": 0
  }
}
Output
package main

import "time"

type Plan struct {
	Name         string `json:"name"`
	Space        int    `json:"space"`
	PrivateRepos int    `json:"private_repos"`
	Collaborators int   `json:"collaborators"`
}

type Root struct {
	Login       string    `json:"login"`
	Id          int       `json:"id"`
	NodeId      string    `json:"node_id"`
	AvatarUrl   string    `json:"avatar_url"`
	PublicRepos int       `json:"public_repos"`
	PublicGists int       `json:"public_gists"`
	Followers   int       `json:"followers"`
	Following   int       `json:"following"`
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`
	Plan        Plan      `json:"plan"`
}

Handling a nullable address field in a customer record

When a customer has not yet filled in their shipping address, the API returns null for that field. Enabling "Use pointers" generates *Address instead of Address, so your code can check if addr == nil rather than comparing against a zero-value struct.

Input
{
  "customerId": "cust_8821",
  "email": "[email protected]",
  "verified": true,
  "orderCount": 5,
  "shippingAddress": null
}
Output
package main

type Address struct{}

type Root struct {
	CustomerId      string   `json:"customerId,omitempty"`
	Email           string   `json:"email,omitempty"`
	Verified        bool     `json:"verified,omitempty"`
	OrderCount      int      `json:"orderCount,omitempty"`
	ShippingAddress *Address `json:"shippingAddress,omitempty"`
}

Related Tools