Free JSON to Go Struct Converter
Parse JSON and automatically generate Go struct definitions with PascalCase field names and json tags for seamless integration with your Go code.
Input & Output
Options
How It Works
- Paste your JSON: Enter any JSON object or nested structure in the input field.
- Get the Go struct: The equivalent Go struct definition with proper field names, types, and JSON tags is generated instantly.
- Copy and use: Copy the struct code and paste it into your Go project to start unmarshalling JSON data.
Why Use JSON to Go Struct Converter?
Writing Go structs by hand for complex JSON responses is tedious and error-prone, especially when the API returns deeply nested objects with dozens of fields. JSON to Go automates this by analyzing the JSON shape and generating correctly typed Go structs with appropriate json:"..." tags that match the JSON field names. This accelerates Go development when consuming REST APIs, processing webhook payloads, or working with any JSON data source.
Features
- Nested struct generation: Deeply nested JSON objects are converted to nested Go struct definitions.
- Type inference: Strings, numbers, booleans, arrays, and null values are mapped to appropriate Go types.
- JSON tags: All fields include json:"fieldName" struct tags matching the original JSON keys.
- Array handling: JSON arrays become Go slices with the correct element type.
- Pointer types: Nullable JSON fields are represented as pointer types (e.g., *string).
Frequently Asked Questions
How does it handle null values?
JSON null fields are converted to pointer types in Go (e.g., *string, *int), which can represent both nil and an actual value. This correctly handles optional or nullable fields in the API response.
What if the JSON has inconsistent types?
If the same field appears with different types across JSON objects (common in real-world APIs), the converter may infer interface{} (any in modern Go). Review these fields and add custom type assertions as needed.
Does it generate the full unmarshalling code?
The tool generates struct definitions. To unmarshal, use json.Unmarshal(data, &myStruct) or json.NewDecoder(r.Body).Decode(&myStruct) with the standard encoding/json package.
A short history of Go and JSON
Go was announced at Google by Rob Pike, Robert Griesemer and Ken Thompson in November 2009; the encoding/json package landed in the standard library before Go 1.0 (March 2012). JSON parsing in Go is based on reflection: you declare a struct, tag the fields with json:"name", and json.Unmarshal walks both the struct and the bytes. The pattern proved so productive that hand-writing structs for large APIs became tedious quickly. Mat Ryer built mholt/json-to-go as a JavaScript-only tool in 2014 (it's still maintained at mholt.github.io/json-to-go) using regex-based JSON parsing in the browser. quicktype (David Siegel, 2017) generalised the idea to 23 languages including TypeScript, Rust, Swift, Kotlin, Python, Java. The performance-focused easyjson (Mail.ru, 2016) skips reflection by generating marshalling code at build time, 4-5× faster than encoding/json for hot paths. Go 1.18 (March 2022) added generics, which simplified some marshalling helpers but didn't change the struct-tag idiom. Go 1.21 (August 2023) added the encoding/json/v2 experimental package (proposal still under discussion as of 2024), aimed at fixing long-standing footguns like silent type mismatches and slow reflection.
Struct vs map[string]interface{}: when each makes sense
- Typed structs win for known shapes. If you control the API or have a stable schema, structs give you compile-time field checking, IDE autocomplete, and 2-3× faster parsing.
json.Unmarshalinto a struct only touches fields it recognises, ignoring unknown JSON keys silently (unless you calldecoder.DisallowUnknownFields). - Maps win for arbitrary JSON.
map[string]interface{}(or in Go 1.18+map[string]any) handles wildly variable shapes, webhook payloads from many sources, plugin configurations, MongoDB documents. Trade-off: every access needs a type assertion (data["age"].(float64)), and numbers parse asfloat64by default, losing precision for large integers. - Hybrid: known fields as struct, unknown as map. Use
json.RawMessageto defer parsing or add a catch-allExtra map[string]interface{} \`json:"-,inline"\`field (with help from libraries likego-restful). Stripe and Twilio's Go SDKs use this pattern for forward-compatible API responses. - Performance gap. Hot paths (e.g. parsing 10,000 webhook events per second) benefit from generated marshallers:
easyjson,ffjson,go-json,sonic(ByteDance, 2022, uses JIT to generate machine code at runtime, fastest JSON library in Go). Generic struct-based parsing handles maybe 100-200 MB/s;sonichandles 1-2 GB/s. - Streaming for huge JSON. A 1 GB JSON file doesn't fit in memory.
json.Decoder.Token()reads token-by-token.json.Decoder.More()walks array elements one at a time. Use streaming for log files, large datasets, ND-JSON streams (one JSON object per line, used by OpenSearch, BigQuery, ChatGPT API).
Where JSON to Go conversion saves real time
- REST API integration. Stripe, GitHub, Slack, Notion all return deeply nested JSON. Grabbing a sample response and pasting into json-to-go produces 80-90% of the final struct. Spend the saved time on the 10% (custom unmarshalers for time formats, omitempty semantics, optional pointer fields).
- Webhook handlers. A typical Stripe webhook payload is 80-200 fields with 3-5 levels of nesting. Hand-typing the Go struct takes 30-60 minutes; pasting the sample payload through json-to-go takes 30 seconds.
- Configuration parsing. JSON config files (preferred over TOML by AWS Lambda, Docker Compose's
compose.json, devcontainer.json) map cleanly to Go structs.encoding/jsonhandles both reading and writing the file with the same struct. - gRPC and Protobuf bridges. Generated protobuf Go types include
jsontags. When transcoding between protobuf and JSON (gRPC-Gateway, Buf, Connect), json-to-go helps draft the Go side from the JSON wire format. - Database JSONB columns. PostgreSQL
jsonb, MongoDB documents, MySQL JSON columns. Thedatabase/sqldriver returns[]bytewhich you unmarshal into a struct. Generators speed up the initial pass for schema-on-read tables. - Fixture data for tests. A real production JSON response becomes the test fixture. json-to-go gives you the struct to unmarshal into for assertions. Combined with
testdata/directories (Go convention since 1.10), keeps tests close to reality. - CSV-to-JSON-to-struct pipelines. For data engineering tasks: read CSV, marshal as JSON for inspection, paste into json-to-go to get the struct, then write a typed pipeline. Far faster than guessing column types from a spreadsheet.
Common mistakes after generating structs
- Forgetting time.Time handling.
encoding/jsonassumes RFC 3339 format (2026-05-13T12:34:56Z). Most APIs deliver this but some use Unix timestamps (Stripe), ISO without timezone (legacy Microsoft APIs), or custom strings. For non-RFC-3339, define a custom type withUnmarshalJSONmethod. - Integer overflow with float64 default. A JSON number parsed into
interface{}becomesfloat64; numbers above 2^53 lose precision. Stripe customer IDs, Twitter snowflake IDs, Discord user IDs (all 64-bit) needjson.Numberorint64in your struct. Usejson.Decoder.UseNumber()for safe parsing intointerface{}. - Misunderstanding
omitempty.omitemptyomits the field on marshal if it's the zero value (empty string, 0, nil pointer, empty slice/map). It does not mean «optional on unmarshal». Anintfield with value 0 is indistinguishable from missing; use*intif you need to know whether the API omitted the field or sent zero. - Field-name case mismatches. Go fields must be exported (capitalised) to be marshalled. The default JSON name is the Go name verbatim, so
UserIDbecomes"UserID"in JSON unless tagged. Generators addjson:"userId"tags to bridge Go'sPascalCasewith JSON'scamelCase. Forgetting the tag means your code «works» with itself but fails against external APIs. - Over-using pointer types. Generators sometimes default to
*stringor*intfor safety. This makes every access need a nil-check. If the API guarantees the field is always present (read the API docs), use the value type and skip the indirection. - Inconsistent types across responses. Some APIs return
tags: []string{"foo"}in one response andtags: "foo"as a string in another. Pure code generation can't bridge this. Write a customUnmarshalJSONthat handles both cases, or change tointerface{}and document the variability. - Silent dropping of unknown fields. By default,
json.Unmarshalignores JSON keys not in the struct. This is often a bug: the API added a field, your code doesn't know. Usedecoder.DisallowUnknownFields()in tests to catch drift; in production keep it lenient.
More frequently asked questions
How does this differ from the canonical mholt/json-to-go?
Same core algorithm: parse the JSON, infer types from the first occurrence of each field, generate a struct with backtick-tagged fields. This implementation runs entirely in your browser without analytics or network calls; the canonical one is hosted at mholt.github.io/json-to-go, also browser-only but on a different domain. Output should match for ~95% of inputs; edge cases (mixed-type arrays, deeply nested anonymous structs) may differ slightly. If you need an exact mholt output, use that; if you need privacy guarantees that the JSON never leaves your device, use this.
Why are some fields generated as interface{}?
Three usual reasons. First, null literal in the source JSON: the generator can't infer a useful type from null alone, so it falls back to interface{}. Replace with a typed pointer (*string, *int) once you know the actual schema. Second, mixed-type arrays: [1, "two", true] can't be a typed slice. Third, missing the field entirely in your sample, paste a more representative JSON if available. In Go 1.18+ you can replace interface{} with the cleaner any alias; they're identical at the type level.
Can I round-trip JSON through this struct without losing data?
Generally yes, with caveats. encoding/json preserves field values but not field order (JSON spec says field order is irrelevant; Go maps are deliberately random). Numeric precision: int64s above 2^53 round-trip safely as Go int64, but if you ever cast to float64 or send through JavaScript (e.g. browser intermediary) you lose digits. Unknown JSON fields are silently dropped on unmarshal-then-remarshal, preserve them with a map[string]json.RawMessage catch-all if round-trip fidelity matters.
When should I use generated struct code (easyjson, sonic) instead of encoding/json?
Default to encoding/json: it's good enough for 95% of services and ships with the language. Switch to easyjson or sonic when you've profiled and JSON marshalling shows up in your CPU flame graph at >10% of total time. Typical breaking points: HTTP services handling thousands of requests per second, log aggregators ingesting GBs/hour, real-time feeds. Both alternatives keep the same struct definitions and tags, so you can swap them in or out without changing field declarations.
Is my JSON sent to any server?
No. JSON parsing and Go code generation both run in JavaScript on your device. Open the Network tab in DevTools while pasting; you'll see zero outbound requests. Safe for proprietary API responses, customer data fixtures, webhook payloads with PII, and anything else covered by an NDA.