# How to Convert JSON to Go Struct (serde-Style Guide)

URL: https://pdfflare.com/blog/convert-json-to-go-struct
Published: May 2, 2026
Reading time: 8 min read

> JSON to Go struct generator — emits serde-style json: tags from any JSON sample. Browser-based, no signup, free.

---

Looking for a JSON to Go struct generator? Here's the path from sample to typed code. You ship a Go service that consumes a third-party JSON API. Vendor docs are out of date; production payloads have fields the docs forgot. Hand-typing the structs from sample data is error-prone busywork. Generate them.

In this guide you'll learn how to convert JSON to a Go struct using [PDFFlare's JSON to Go tool](https://pdfflare.com/tools/json/json-to-go) — how `encoding/json` uses struct tags to map keys, why exported fields with tags are the idiomatic pattern, how the inference picks `int` vs `float64`, and the cases where you'll want to refine manually.

## Why Struct Tags Matter

`encoding/json` matches JSON keys to struct fields by name unless a `json:"original_key"` tag tells it otherwise. Idiomatic Go uses PascalCase exported field names (so the package outside the file can see them); the tag preserves the original snake_case (or any-case) JSON key. Without the tag, snake_case JSON never binds to PascalCase Go.

## How to Convert JSON to Go (Step by Step)

1. **Open [PDFFlare's JSON to Go tool](https://pdfflare.com/tools/json/json-to-go)**.
2. **Paste a JSON sample.** Real production payload — samples often miss optional fields.
3. **Click Convert to Go.** One `struct` per unique shape; `json:"..."` tags on every field; type inference for ints vs floats.
4. **Drop into your project.** `json.Unmarshal(data, &v)` binds the bytes to your struct.

## Real Use Cases

### Typing a third-party REST API

Hit the endpoint, copy the response, paste here, get accurate Go types. Works whether the upstream is documented or not.

### Webhook handlers

Stripe, GitHub, and similar services document webhooks as JSON examples. Convert each example to a Go struct so handlers stay compile-time safe.

### Migrating from a dynamic language

Porting a Python or Node service to Go? Use real production payloads to seed type definitions instead of guessing field names.

### CLI tools that consume JSON

Building a CLI that pipes JSON from another tool? Generate the input schema as Go structs in seconds.

## Common Mistakes (and How to Avoid Them)

- **Forgetting `omitempty`.**Add it manually to fields you don't want emitted when zero (e.g. `json:"name,omitempty"`).
- **Using float64 for currency.** Refine to a decimal type (`shopspring/decimal` is common) for money fields.
- **Treating mixed-type arrays as a final answer.** They become `[]interface`; refine to a typed wrapper if you know the shape.
- **Renaming fields without updating tags.** The field name changes affect Go reflection but the JSON tag controls wire format. Update both consciously.

## Privacy: Your JSON Stays Local

Conversion runs in your browser. Safe for production payloads.

## Related Workflows in the JSON Suite

Adjacent tools you might find useful while working on the same JSON document: the [JSON to Rust](https://pdfflare.com/tools/json/json-to-rust) and [JSON Schema](https://pdfflare.com/tools/json/json-schema) both pair well with the conversion above. The first handles a different output format that consumers of your data may prefer; the second covers the validation side of the same workflow.

## Related Tools

- [JSON to Rust](https://pdfflare.com/tools/json/json-to-rust) — same idea with serde derive macros.
- [JSON to TypeScript](https://pdfflare.com/tools/json/json-to-typescript) — matching front-end types.
- [JSON to Protobuf](https://pdfflare.com/tools/json/json-to-protobuf) — Go has excellent gRPC support; bring the same payload to Protobuf for binary speed.
- [JSON Formatter](https://pdfflare.com/tools/json/json-formatter) — pretty-print before converting.

## Idiomatic Go Patterns for JSON Handling

Go has a long tradition of pragmatic JSON handling, and a generated struct is a starting point that benefits from some idiomatic refinements. The first refinement is naming: Go convention is short, descriptive names, often with no articles (User, not TheUser; Order, not OrderInfo). The generator preserves whatever names appear in the source data, which often includes prefixes or suffixes that read fine in JavaScript or Python but feel wordy in Go. Rename aggressively after generation; idiomatic names make Go code feel native rather than ported.

The second refinement is interface design. Generated structs are concrete types. Production Go often uses interfaces to decouple consumers from concrete types, enabling testability and flexibility. Once your generated types stabilize, ask which behaviors they support and whether those behaviors should be expressed as interfaces. This is not always worthwhile — interfaces add indirection — but for code that touches multiple data sources or has complex testing needs, the indirection pays off.

The third refinement concerns error handling. The standard encoding/json package returns errors when decoding fails, but the errors are notoriously generic. Production Go services usually wrap these errors with context using fmt.Errorf and the percent-w verb. After decoding, validate domain invariants and return rich errors that explain not just that decoding failed but what specifically was wrong with the input. This investment in error messages pays back many times over when something breaks in production and you need to diagnose from logs.

Finally, consider streaming for large payloads. The generator's output works well with json.Unmarshal, which loads the entire document into memory. For megabytes of JSON, streaming with json.Decoder is more memory-efficient and lets you start processing before the full document arrives. The generator's struct shape stays the same; only the decode call changes. Knowing this option exists matters when payload sizes grow unexpectedly.

## Production Patterns for JSON to Go Structs

A generated struct is a starting point. Production-grade Go code adds:

### Pointer vs Value for Optional Fields

Go's zero values (`0`, `""`,`false`) are indistinguishable from missing JSON fields. For genuinely optional fields, switch the type to a pointer (`*int`, `*string`) — now`nil` means missing, value means present.

### Custom UnmarshalJSON for Edge Cases

When the API uses inconsistent shapes (a field that's sometimes a string, sometimes a number), implement `UnmarshalJSON([]byte) error` on the struct. Inside, try each shape in order. Cleaner than `map[string]interface{}` spelunking.

### Use Tags for Multi-Format Marshaling

The generator emits `json:"..."` tags. If you also serialize to YAML or BSON (MongoDB), add those tags alongside: `json:"name" bson:"name"`. One struct serves multiple wire formats.

## When to Use a Different Approach

A few alternatives to know:

- **For protobuf-based services**, generate a proto schema with [JSON to Protobuf](https://pdfflare.com/tools/json/json-to-protobuf) — protoc emits Go code with full proto3 semantics.
- **For OpenAPI-driven workflows**, generate from the spec instead — full coverage of every endpoint.
- **For schema-first validation**, generate a [JSON Schema](https://pdfflare.com/tools/json/json-schema) first, then point a Go schema generator at it.

## Common Mistakes to Avoid

1. **Using `interface{}` as a catch-all.** Tempting when JSON shape varies, but loses type safety. Use a custom UnmarshalJSON or a discriminated-type pattern instead.
2. **Forgetting `omitempty`.** When serializing back, empty fields show up in the output unless tagged `json:"name,omitempty"`. The generator can emit this; remember to enable it.
3. **Treating numbers as `float64` by default.**Go's json package decodes all numbers as `float64` by default. If your IDs are int64-shaped, use `json.Number` or unmarshal into an explicit `int64` field.
4. **Skipping validation.** The decoder happily accepts a payload with missing required fields. Validate after Unmarshal — go-playground/validator with struct tags (`validate:"required"`) is the standard.
5. **Mismatching exported field names.** Go field names must be capitalized to be visible to the json package. The generator does this correctly; if you rename a field to lowercase by mistake, decoding silently fails for that field.

## Real-World Use Cases

- **HTTP handler request/response binding.** Generate the request body struct, decode into it in your handler, type-safe end to end.
- **SDK clients for third-party APIs.** Stripe, GitHub, etc. — generate matching Go structs from their documented payloads, ship as a small package.
- **Replacing `map[string]interface{}` spelunking.** Existing handlers casting through the generic map are fragile; replace with a typed struct and the compiler catches refactor breakage.
- **Cross-service contracts.** Service A emits JSON; Service B consumes it. Generate matching Go structs from a shared sample, version-control them, both services stay in sync.

## Polishing the Generator's Output

Go encourages a particular style of working with data: small, focused types, explicit error handling, and a preference for composition over inheritance. Whatever your specific use case, treat the generated output as a draft that deserves a careful read-through. Generators are excellent at producing the mechanical structure of an artifact and not at the editorial decisions that make the difference between something a colleague will tolerate and something a colleague will appreciate. Read every section of the output the way you would read a piece of writing you were proofreading for a friend. Look for inconsistent naming, missed opportunities to consolidate similar items, and places where the structure is mechanically correct but conceptually awkward. The five minutes spent on this review are the difference between an artifact that pays back over months and one that needs a second pass before it can be used. The generator handles the heavy lifting; you handle the polish that turns a draft into a deliverable. This division of labor is what makes generated code worthwhile in the first place. Without that final pass of human editorial judgment, the generator's output is merely fast rather than valuable, and the value matters more than the speed in nearly every real production setting.

The same logic applies to documentation, comments, and inline context that your generator output rarely supplies. A generated artifact has structure but no narrative; the narrative is what makes the thing useful to the next person who reads it. Add the few sentences of context that explain why a particular choice was made, what the surrounding system expects, and what the next person should look out for. These small editorial gestures cost almost nothing in the moment and pay back many times over when someone is trying to understand what you produced months later. Treat generation as the first ten percent of the work and these editorial passes as the remaining ninety percent that turns mechanical output into something a colleague will reach for again and again. Build the habit early and the gap between your generated artifacts and hand-written ones gets very small over time, which is the real prize.

## Wrapping Up

A clean Go struct with json tags is the start of every well-typed JSON consumer. [PDFFlare's JSON to Go tool](https://pdfflare.com/tools/json/json-to-go) generates it in two clicks; refine for omitempty, decimal types, and stronger array element types where the inference can't.

---

## Frequently asked questions

**Q: How does encoding/json use struct tags?**

A: encoding/json matches JSON keys to struct fields by name unless a json:"original_key" tag tells it otherwise. The tag preserves the original snake_case JSON key while the field name uses idiomatic Go PascalCase, so the field is exported and binding still works.

**Q: How are integer vs floating-point types distinguished?**

A: Whole-number values become int (Go's default integer); decimal values become float64. JSON treats both as 'number' but the converter inspects each value. For 64-bit IDs, refine to int64 or uint64 manually — int's size depends on platform.

**Q: What about omitempty?**

A: PDFFlare doesn&apos;t add omitempty by default — adding it changes serialization behaviour for zero values. Add it manually to fields you don&apos;t want emitted when zero (e.g. json:"name,omitempty"), but be aware that distinguishing 'zero' from 'absent' requires pointer types or a custom UnmarshalJSON.

**Q: Are arrays of mixed types handled?**

A: Yes — they fall back to []interface{}. Refine to a typed wrapper or a sealed interface manually if you know the discriminator. Most JSON APIs are uniform-typed within an array, so the fallback is rarely the final answer.

**Q: Is my JSON sent to a server?**

A: No. Conversion runs entirely in your browser — safe for production payloads.

---

## About PDFFlare

PDFFlare is a free collection of online tools for working with PDFs, images, text, JSON, and developer utilities. All tools run client-side in your browser — no signup, no upload to our servers, no rate limits.

For the full site index, see https://pdfflare.com/llms.txt.
For the complete content dump in one file, see https://pdfflare.com/llms-full.txt.