The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →With Go’s traditional encoding/json API, a pointer field alone cannot tell you whether a JSON member was omitted or explicitly set to null: both commonly decode to nil. To preserve all three states—missing, null, and a concrete value—check object-member presence separately, for example by decoding into map[string]json.RawMessage.
What Go’s usual struct decoding preserves
JSON has three relevant states for a member: it can be absent, present with null, or present with a value. A Go field’s zero value does not record which input state produced it.
As an Amazon Associate I earn from qualifying purchases.
Pointer fields
For a fresh struct decoded with the traditional encoding/json API, an absent pointer field remains nil. An explicit JSON null also leaves a pointer nil, so the pointer does not distinguish those cases. The Go project’s tutorial explains the absent case: “If there were a Bar field in the JSON object, Unmarshal would allocate a new Bar and populate it. If not, Bar would be left as a nil pointer.” Go JSON tutorial
Use *T when the distinction you need is simply whether a non-null value was decoded. Do not treat it as a three-state presence indicator.
#1 Best Overall
Scalar fields
For a fresh struct, an omitted scalar remains at its zero value. In v1, unmarshaling JSON null into a non-pointer scalar has no effect, so it also remains at that zero value. For example, a missing name and a null name can both leave a string field empty.
Decode into a newly initialized destination when behavior must not depend on old field contents. Decoding into a reused value can preserve preexisting state where input does not update a field, making conclusions based on the resulting value unreliable.
Choose a representation based on the states you need
| Requirement | Representation | What it preserves |
|---|---|---|
| Know whether a non-null value was populated | *T field |
Nil versus decoded non-null value; not missing versus explicit null in the common v1 case. |
| Distinguish missing, null, and value | map[string]json.RawMessage and key lookup |
Member presence, raw JSON payload, and—after decoding—its typed value. |
| Keep a typed struct interface and track presence | Custom wrapper with UnmarshalJSON |
All three states, if the wrapper explicitly records presence and null/value state. |
| Read an open-ended object before choosing fields | map[string]json.RawMessage or a generic JSON map |
Object member presence and raw payloads; selected values still need validation. |
Distinguish the three states with RawMessage
A map lookup’s boolean result tells you whether the key exists. If it does, compare the raw JSON to null; otherwise decode the non-null value into the field’s actual type. This keeps presence separate from type conversion.
import (
"bytes"
"encoding/json"
)
var fields map[string]json.RawMessage
if err := json.Unmarshal(data, &fields); err != nil {
return err
}
raw, present := fields["name"]
switch {
case !present:
// The member was missing.
case bytes.Equal(bytes.TrimSpace(raw), []byte("null")):
// The member was present with explicit JSON null.
default:
// The member was present with a non-null JSON value.
var name string
if err := json.Unmarshal(raw, &name); err != nil {
return err
}
// Use name.
}
The imports are bytes and encoding/json. Return type errors from decoding rather than silently accepting a value of the wrong JSON type. If the endpoint requires the top-level input to be an object, validate that contract too; decide explicitly how it should handle a top-level null or another JSON type.
When a custom wrapper is clearer
For a typed API, define a wrapper whose UnmarshalJSON method records whether the field was present and whether its payload was null or a concrete value. A mere value field inside the wrapper is not enough: its zero value still cannot indicate absence. For many fields or nested objects, a typed wrapper—or a two-pass decode that first captures presence and then populates typed values—can be easier to maintain than repeated map checks.
Apply the states to PATCH semantics
Go does not choose what missing or null means for an application. A common PATCH contract is that a missing member leaves the stored value unchanged, null clears it, and a concrete value replaces it. The handler must receive a representation that preserves those distinctions, then implement the contract deliberately. If an API instead treats null and missing alike, a pointer may be sufficient for that narrower requirement.
Rank #4
Keep the JSON API version explicit
These decoding details describe the traditional encoding/json API, commonly called v1. Go documents encoding/json/v2 separately, and its behavior can differ, including null handling and merging into preexisting values. The Go blog’s August 2026 note says Go 1.27 introduces the v2 package; check the package documentation for the exact Go toolchain and API you target rather than carrying v1 assumptions across. Go blog: JSON v2
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallLikewise, omitempty is a marshaling option, not a way to detect whether an input field was present. The v1 and v2 APIs define omission differently when marshaling, but neither gives omitempty a presence-detection role during unmarshaling. See the v1 package documentation and v2 package documentation.
Quick Recap
Best Value
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




