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.
| Wire state | Example object | Schema 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 schema | default: "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.
| Contract field | Synthetic choice | Review question |
|---|---|---|
presence | required or optional | Does omission mean a contract failure? |
types | string plus null | Is null distinct from omission? |
empty_string | reject or allowed | Can blank text be a valid value? |
empty_collection | known_empty or not_allowed | Does an empty list carry information? |
default_owner | consumer or producer | Which side applies the default? |
invalid_action | reject_and_report | Can an ambiguous state enter downstream logic? |
{
"$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.”
| Fixture pair | Expected 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 default | Annotation 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.
| Artifact role | Meaning to test | Common trap |
|---|---|---|
| Snapshot | The object describes the current exported state | Omission silently treated as deletion |
| Full replacement | The object replaces the prior representation | Optional fields disappear without intent |
| Merge patch | Members describe changes to apply | Null removes a member rather than storing null |
| Partial update | Omission means leave unchanged | Null 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
- Internet Engineering Task Force: The JavaScript Object Notation (JSON) Data Interchange Format (opens in a new tab)
- JSON Schema: JSON Schema Validation: Draft 2020-12 (opens in a new tab)
- JSON Schema: JSON Schema: Annotations and Metadata (opens in a new tab)
- Internet Engineering Task Force: JSON Merge Patch (opens in a new tab)