Standard library patterns · 3 MIN READ

Strict JSON input: reject ambiguity before side effects

Separate JSON syntax acceptance, unknown-member policy and domain validation.

Successful decoding proves only that input was accepted under the decoder policy. It does not authorize the caller or establish a valid business transition. V2 rejects duplicate names and invalid UTF-8 by default; exact field-name matching and unknown-member rejection are separate settings.

Strict JSON input: reject ambiguity before side effects · A MAP OF THE IDEA

  1. Bound input bytes
  2. Decode a fresh value
  3. Validate and authorize
  4. Publish only success

Useful when

  • Separate JSON syntax acceptance, unknown-member policy and domain validation.
  • Bound input, decode under an explicit policy, validate the domain and authorize the target before performing a side effect. Test each boundary independently.

Watch out

  • Strict decoding cannot authorize a resource or make an external write exactly once.

Implementation & operation cost: Choose the policy from the actual contract, then verify its failure path in the target environment.

01

Make the boundary policy visible

Unknown members are ignored by default in v2; use RejectUnknownMembers when the endpoint contract requires strict commands. A forward-compatible event consumer may need another policy. Bound body bytes before decoding and limit expensive domain work separately.

go
func decodeName(data []byte) (string, error) {
	var request struct {
		Name string `json:"name"`
	}
	if err := jsonv2.Unmarshal(data, &request, jsonv2.RejectUnknownMembers(true)); err != nil {
		return "", fmt.Errorf("decode name: %w", err)
	}
	name := strings.TrimSpace(request.Name)
	if name == "" {
		return "", errors.New("name required")
	}
	return name, nil
}
Read the code

The complete helper needs encoding/json/v2 aliased as jsonv2, fmt, strings and errors. Its caller must cap data size before allocation. A fresh local target prevents earlier values or a partial failed decode from becoming a successful published result.

02

Test syntax and business meaning separately

Use fixtures for duplicate names, differently cased names, an unknown property, invalid UTF-8, a trailing second JSON value and a blank name. Record acceptance, typed value and domain-validation result. Do not decide compatibility solely from a changed error string.

03

Authorize after decoding, before acting

A valid document or resource ID does not establish tenant access. Bind the caller scope to the authoritative lookup or update predicate. A timeout during a later external write can leave its result unknown, so strict JSON decoding is not a substitute for an idempotency protocol.

04

Fuzz a meaningful contract

For arbitrary bounded bytes, assert that failed decoding returns no usable partial value. For accepted requests, check required invariants and a typed encode/decode round trip. Seed the ambiguity cases explicitly; a ten-second campaign explores inputs without proving every future input safe.

TAKE THIS WITH YOU

Bound input, decode under an explicit policy, validate the domain and authorize the target before performing a side effect. Test each boundary independently.

Check your understanding

A v2 command endpoint must reject additional properties. What should its decoder do?

Go to the source

Progress stays in this browser. No account needed.