Every value found at the same position is merged into one description before any Go is written, so an array of objects gives one struct typed from all of its elements, not the first. Numbers are where Go is strictest. A field whose samples are all written as integers, with no decimal point or exponent, and fit in int64 is int64; a single 2.5 makes it float64, because encoding/json will not decode 1.0 or 1e3 into an int64 and fails with "cannot unmarshal number 1.0 into Go struct field". An integer beyond int64, or a number beyond float64, is json.Number, which keeps the digits exactly as written. A field whose JSON type differs between samples, such as a number in one and a string in another, is any, with a warning: encoding/json fills an any with float64 for every number, so an ID of 9007199254740993 read into an any comes back as 9007199254740992 unless the Decoder calls UseNumber.
A field that is null in some samples, or missing from some objects, is a pointer such as *string or *Address. Without one, null and a missing key both decode to the zero value, and a real 0, "" or false can no longer be told apart from no value at all; with one, nil means null or absent and anything else is a value. Slices, maps and any are not wrapped, because nil already stands for null there. A key missing from some samples also gets ,omitempty, so marshalling leaves it out instead of sending null. On a pointer omitempty drops only nil, so a present 0 is still written; on a slice it drops an empty one as well, so [] goes back out as a missing key, and ,omitzero (Go 1.24) drops only nil if that difference matters. A string field is time.Time only when every sample is an RFC 3339 date-time with seconds, a capital T and a Z or offset, because that is all time.Time decodes: "", a date on its own or a time without a zone makes the whole Unmarshal fail.
Field names must be exported, so each is PascalCase from its key with golint’s initialisms in capitals: user_id, user-id and userId all give UserID, and avatar_url gives AvatarURL. A name that cannot start an exported identifier gets Field in front, so 2fa gives Field2fa, and keys that produce the same name are numbered UserID2, with a warning. The tag always carries the original key, so marshalling writes the key exactly as the API sent it; decoding still matches keys case-insensitively, as encoding/json always has, so {"ID": 7} also fills a field tagged json:"id". A key that is a single dash is tagged json:"-,", since json:"-" means skip the field, and a key containing a comma, a quote, a backslash or a backquote cannot be named by any tag, so it gets no field and a warning. Nested objects become named types after their keys, shared when two are identical, or inline anonymous structs if you prefer; a root array is a slice type such as type Root []RootItem. The output is laid out as gofmt lays it out, and imports time or encoding/json only when it uses them.