The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →For a PATCH endpoint, treat a field as having three possible inputs: omitted, explicitly null, or present with a value. JSON has no undefined literal; developers often use “undefined” to mean that an object member was omitted. A normal Go struct field—including a pointer—does not preserve all three states after ordinary JSON decoding. Choose the endpoint’s patch semantics first, represent field presence explicitly, validate the proposed resource, and persist the change only after the whole update passes.
Why a pointer does not distinguish omitted from null
JSON objects may omit a member or include it with the value null. Those inputs can mean different things in an update: omission commonly means “leave unchanged,” while null may mean “clear this value.” JSON itself does not have an undefined value. The Go encoding/json package’s ordinary unmarshalling does not retain a separate omitted-field marker for a struct field; for a pointer field, both an omitted member and explicit null leave the pointer nil. See the Go encoding/json documentation.
As an Amazon Associate I earn from qualifying purchases.
That creates a practical problem: a handler cannot infer whether nil means “the client said null” or “the client sent nothing.” Likewise, decoding into a non-pointer field can make an omitted field indistinguishable from a supplied zero value. For PATCH, that matters for legitimate updates such as setting enabled to false, quota to 0, or label to an empty string.
Recommended Free Tools
Choose the patch format and null behavior
Before writing the Gin handler, specify what omission and null mean for each field. If interoperability is important, consider one of the established patch media types instead of an application-defined object format.
#1 Best Overall
| Format | Request shape | Omission | Clearing or removing | Useful when |
|---|---|---|---|---|
| Custom presence-aware object | Resource-like JSON object with application-defined rules | Define as unchanged | Define null behavior per field | You need flexible, endpoint-specific rules and will document them consistently. |
| JSON Merge Patch (RFC 7396) | Resource-like patch object | Unchanged | null removes the corresponding target member |
A compact object merge is a natural fit. See RFC 7396. |
| JSON Patch (RFC 6902) | Array of operation objects | No operation means unchanged | Use an explicit remove operation |
Clients need explicit path-level operations such as add, replace, remove, or test. See RFC 6902. |
These formats are not interchangeable. RFC 7396 gives null a removal meaning; RFC 6902 expresses removal as an operation. An ad hoc DTO with custom null rules is neither standard unless it implements the corresponding format and semantics. RFC 7396 states: “Null values in the merge patch are given special meaning to indicate the removal of existing values in the target.”
Represent presence for a custom PATCH object
For a small endpoint with custom semantics, a wrapper can record whether a member appeared, whether its value was null, and—if it was not null—the decoded value:
type PatchField[T any] struct {
Present bool
Null bool
Value T
}
func (p *PatchField[T]) UnmarshalJSON(data []byte) error {
p.Present = true
if bytes.Equal(bytes.TrimSpace(data), []byte("null")) {
p.Null = true
return nil
}
return json.Unmarshal(data, &p.Value)
}
type UpdateUserRequest struct {
Nickname PatchField[string] `json:"nickname"`
}
This is an illustrative sketch, not a complete endpoint. It needs the appropriate imports and application-specific rules. A present member invokes the wrapper’s UnmarshalJSON; an absent member leaves the wrapper at its zero value. With that representation, the handler can distinguish omission from null and from a supplied value. Decide how the wrapper should behave for nested objects, arrays, duplicate keys, and marshaling before treating it as a reusable general-purpose type.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Another option is decoding the request object into map[string]json.RawMessage. A missing map key indicates omission; for a present key, inspect the raw JSON for null or decode it into the expected type. This makes field-name mapping and type checks explicit, but the handler must deliberately handle known and unknown JSON names.
Bind JSON in Gin without losing control of errors
Gin’s JSON binding parses request data, while the patch logic decides what each input means. When the handler needs to choose its own error response, use ShouldBindJSON and handle the returned error. Gin’s must-bind methods abort the request and write an HTTP 400 response on binding errors, so do not accidentally write a second response afterward. Gin documents request binding and validation in Request Binding & Validation.
Gin integrates with go-playground/validator/v10 for validation. For a presence-aware wrapper, make sure the JSON binding path uses the wrapper’s encoding/json unmarshalling behavior as intended. Gin’s documentation about binding a custom unmarshaler also discusses TextUnmarshaler in supported URI and form scenarios; that is not, by itself, a general solution for omitted-versus-null JSON members.
Rank #4
Validate the patch and the resulting resource
Validation has two jobs here. First, check supplied non-null values against their field rules. Second, check business invariants against the complete resource after applying the patch. A PATCH request is partial, so a field that was omitted should not fail merely because it is absent from the request. Null also needs an explicit policy: allow it only for fields the API considers clearable.
- Do not blindly use a
requiredtag on every PATCH field. Validator’s required semantics generally demand a non-zero or non-nil value, which can reject valid assignments such asfalse,0, or an empty string, as well as fields omitted intentionally. - Run field validation only when a field is supplied with a value, unless the endpoint explicitly defines validation for null or absence.
- Use validator’s partial-validation or struct-level facilities where they fit; they do not infer omitted-versus-null meaning for an ordinary struct. See the validator/v10 documentation.
- Apply proposed changes to a copy or newly built resource, then validate cross-field and business constraints against that complete proposed state.
For example, if an update changes a minimum and maximum together, checking each field in isolation may miss that the resulting minimum exceeds the maximum. Validate the proposed pair before saving either change.
Best Value
Apply and persist a PATCH atomically
Do not mutate a persisted model as fields are decoded or validated one by one. Decode the request, interpret presence, apply permitted changes to a copy of the current resource, validate the resulting state, and persist the update as one safe operation or transaction. This order avoids saving a partial update when a later field or invariant fails.
- Load the current resource.
- Decode and validate the request’s syntax and types.
- For each known field, leave it unchanged when omitted; apply a supplied value; and either clear it or reject null according to the endpoint contract.
- Validate field rules for supplied values and business constraints on the proposed complete resource.
- Save the proposed resource atomically; if validation fails, do not persist any part of the patch.
Cases your handler should get right
{}leaves every field unchanged.{"nickname":null}clears the nickname only if the contract declares it clearable; otherwise reject it.{"enabled":false}sets the value to false rather than treating it as omission.{"quota":0}sets zero when zero is permitted.{"label":""}preserves the difference between a supplied empty string and an omitted member.- Malformed JSON and unknown fields have deliberate, documented error behavior.
- A patch that violates a cross-field invariant fails without persisting only some of its changes.
These cases are useful as endpoint tests because they exercise the distinct input states and the all-or-nothing update behavior.
Quick Recap
Further Gin and Go references
- Developing a RESTful API with Go and Gin
- Gin request binding and validation
- Gin custom unmarshaler binding
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.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Repair Windows errors before they cause bigger problems3Scan for outdated or missing drivers - takes under a minute




