Skip to content

JSON serialization review guide

Keep null, missing, empty, and default states distinct in JSON exports

Model property presence and property value as separate parts of an export contract. A missing member, an explicit null, an empty value, and a documented default can trigger different consumer behavior even when a table view makes them look alike.

First decide whether a property is present, then decide what its value means. In JSON, an omitted member is different from a member whose value is null, an empty string, an empty array, or an empty object. JSON Schema required checks presence, while default is an annotation and does not automatically fill a missing property during validation. JSON Merge Patch adds another boundary where null means removal. This guide reviews serialization and schema semantics; it does not replace a domain-specific missing-value dictionary.

Updated

On this page

Separate property presence from property value

Treat an object member as two signals: whether the key is present and, if present, which JSON value it carries. The distinction is structural before it is semantic. A consumer can use omission to mean “not supplied,” null to mean “explicitly no value,” an empty string to mean “a supplied string with no characters,” and an empty array to mean “a supplied collection with no members.” The field owner must define the meaning that fits the contract.

The source dates shown in this guide identify documentation checks on 22 October 2026, not original publication dates. This guide intentionally stays at the serialization boundary. It does not decide whether a domain label such as “unknown” or “not applicable” is consistent across a dataset; use the owner’s field dictionary for that separate question.

Separate property presence from property value (table)
Wire stateExample objectSchema or consumer question
Missing{}Is the member optional, or is the export incomplete?
Explicit null{"status": null}Is null allowed, and does it mean cleared or unknown?
Empty string{"status": ""}Is an empty string valid, rejected, or a separate state?
Empty array{"items": []}Does presence of an empty collection mean a known empty result?
Empty object{"meta": {}}Is an empty nested object meaningful or a serializer artifact?
Default in schemadefault: "pending"Who applies it, and is the result written back?

Write a state contract for each field

A useful contract names allowed presence, allowed JSON types, empty-value behavior, default ownership, and the action for an invalid combination. Do not put a default in a schema and assume that every validator or importer materializes it. If a consumer wants a materialized value, record the transformation as an explicit step and preserve whether it came from the source or the defaulting process.

Write a state contract for each field (table)
Contract fieldSynthetic choiceReview question
presencerequired or optionalDoes omission mean a contract failure?
typesstring plus nullIs null distinct from omission?
empty_stringreject or allowedCan blank text be a valid value?
empty_collectionknown_empty or not_allowedDoes an empty list carry information?
default_ownerconsumer or producerWhich side applies the default?
invalid_actionreject_and_reportCan an ambiguous state enter downstream logic?
Write a state contract for each field code example, JSON
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "status": { "type": ["string", "null"] },
    "items": { "type": "array" }
  },
  "required": ["status"],
  "additionalProperties": false
}

This synthetic schema requires the status member but permits either a string or null. It does not make an omitted member equal to null, and it does not decide whether an empty string means anything useful. JSON Schema validation supplies structural evidence; the field owner still supplies the semantic contract JSON Schema, (opens in a new tab).

Test serializers with state-preserving fixtures

Build fixtures that differ by one state at a time. Run them through the producer serializer, transport, parser, validation, and any database or spreadsheet adapter. Compare key presence as well as the decoded value. A serializer that omits nulls may appear harmless until a consumer uses omission to mean “leave the previous value unchanged.”

Test serializers with state-preserving fixtures (table)
Fixture pairExpected distinction
{} and {"status":null}Absent is not explicit null
{"status":null} and {"status":""}Null is not an empty string
{"items":[]} and {}Known empty collection is not omission
{"meta":{}} and {}Empty object presence is visible
Missing field and schema defaultAnnotation is not automatic materialization

Evidence to record

  • The exact input object and whether each member was present.
  • The serialized bytes or approved redacted representation.
  • The validator result and schema version.
  • Any defaulting transformation, including who applied it.
  • The consumer action for every accepted and rejected state.

The JSON Schema annotations guide explicitly treats default as metadata about an intended value, not a validation-time fill operation. A passing validation result therefore does not prove that the consumer will see a populated field JSON Schema, (opens in a new tab).

Check update and patch semantics separately

An export can be correct as a snapshot and still be wrong as an update document. If a pipeline uses JSON Merge Patch, an explicit null is not merely a nullable value: it requests removal of the target member. RFC 7396 also cautions that the format is a poor fit for objects that need explicit null values. Record whether the artifact is a snapshot, a replacement, or a patch before interpreting null.

Check update and patch semantics separately (table)
Artifact roleMeaning to testCommon trap
SnapshotThe object describes the current exported stateOmission silently treated as deletion
Full replacementThe object replaces the prior representationOptional fields disappear without intent
Merge patchMembers describe changes to applyNull removes a member rather than storing null
Partial updateOmission means leave unchangedNull and omission collapsed by serializer

RFC 7396 says null in a merge patch removes the named member and notes the format is not suitable when explicit null values must be represented. Keep patch semantics out of a snapshot schema description, or consumers will apply the wrong state transition Internet Engineering Task Force, (opens in a new tab).

Take a qualified next step

If the owner can describe the collection and its handling authority, the next step may be the appraisal route. That route is a request for review, not automatic acceptance, a quote, a buyer match, a legal opinion, or a license. A synthetic object matrix is enough to expose state collapse; do not send private records merely to test whether null survived serialization.

Takeaways

  • Treat key presence and value as separate contract dimensions.
  • Distinguish null, empty string, empty collection, empty object, and omission.
  • Treat default as an explicit transformation owned by a named side.
  • Test snapshot and patch documents under their actual semantics.
  • Keep wire-state findings separate from domain missingness labels.

Sources

  1. Internet Engineering Task Force: The JavaScript Object Notation (JSON) Data Interchange Format (opens in a new tab)
  2. JSON Schema: JSON Schema Validation: Draft 2020-12 (opens in a new tab)
  3. JSON Schema: JSON Schema: Annotations and Metadata (opens in a new tab)
  4. Internet Engineering Task Force: JSON Merge Patch (opens in a new tab)

Use the appraisal route only after JSON state semantics are documented.

Preparation does not promise acceptance, a buyer, a quote, or an offer.