Cryonel

Generate nested Go structs from JSON

A JSON sample can produce a useful Go starting point, but nested objects, nulls, optional fields, numbers, and dates still need contract-aware review.

Start with representative JSON

Include the object layers and array values your application actually receives. A generator can infer []string from a non-empty string array, but an empty array provides no element evidence. One response also cannot reveal properties that are optional or omitted in other cases.

{
  "id": 42,
  "profile": {"display_name": "Ada"},
  "roles": ["admin", "editor"]
}

Cryonel’s JSON to Go converter produces exported fields and preserves original JSON keys in struct tags.

Exported fields and JSON tags

The standard encoding/json package can populate exported fields whose names begin with an uppercase letter. A key such as display_name becomes a Go identifier such as DisplayName with a tag json:"display_name". Keep the tag when the wire name and Go name differ.

Named versus anonymous nested structs

An inline anonymous struct keeps generated code in one place and works well for a response used once. If the same nested concept appears in several operations, extract it into a named type. Named types are easier to test, document, reuse, and attach methods to. Do not create a shared type only because two samples happen to have the same fields; confirm they represent the same domain concept.

Null and optional values

A JSON null value does not reveal its intended non-null type. A generator may use interface{} as a conservative placeholder. Replace it using the API contract: a pointer can distinguish missing or null from a zero value, while a concrete value is simpler when the field is guaranteed. Database nullable wrappers are not automatically the right choice for HTTP models.

Numbers and time strings

JSON has one number syntax, while Go has many integer and floating-point types. Large identifiers may need strings to avoid precision loss in other systems. Decimal money values should not automatically become float64 without a domain decision. ISO date-time strings remain strings unless a custom unmarshal or time.Time contract is intentionally added.

Verify unmarshalling

Add a small test that unmarshals representative and edge-case payloads, then checks nested values. Include absent fields, null, empty arrays, and unknown properties. Go ignores unknown fields by default; use a decoder configured to reject them when strict contract enforcement is required.

Frequently Asked Questions

Why must fields be exported?

The standard encoding/json package can only populate exported struct fields.

What type should JSON null use?

The sample alone cannot decide. Choose a pointer, wrapper, interface value, or concrete type from the contract.

Should every nested object be anonymous?

No. Shared domain concepts should usually become named reusable types.

Related Tools and Guides