Specification scope: Core only.

JSON Schema can describe a map, but it cannot bring itself to call one a map. You assemble one from object keywords and leave tools to infer that the object is meant to be a dictionary.

A JSON array may be a sequence or a set. A JSON object may be a record or a dictionary. You cannot tell which contract applies by looking at the brackets.

JSON Structure makes the choice explicit. An array preserves order, a set requires unique elements without assigning them an order, and a map holds values under dynamic string keys. Consumers can then expose the operations the model actually permits instead of guessing from a sample payload.

Consider a playlist service. Its play queue has positions and may repeat a track. Its list of licensed territories has neither property. Per-device volume settings arrive under device names that the schema author cannot know. Calling all three of these “collections” is accurate and not terribly helpful.

One model, three collection types

The playlist schema assigns each collection its own contract:

{
  "$schema": "https://json-structure.org/meta/core/v0/#",
  "$id": "https://example.com/schemas/playlist",
  "name": "Playlist",
  "type": "object",
  "properties": {
    "title": { "type": "string" },
    "playQueue": {
      "type": "array",
      "items": { "type": { "$ref": "#/definitions/Track" } }
    },
    "licensedTerritories": {
      "type": "set",
      "items": { "type": "string" }
    },
    "deviceVolume": {
      "type": "map",
      "values": { "type": "uint8" }
    }
  },
  "required": ["title", "playQueue", "licensedTerritories", "deviceVolume"],
  "additionalProperties": false,
  "definitions": {
    "Track": {
      "name": "Track",
      "type": "object",
      "properties": {
        "id": { "type": "string" },
        "title": { "type": "string" }
      },
      "required": ["id", "title"],
      "additionalProperties": false
    }
  }
}

An instance looks ordinary:

{
  "title": "Night train",
  "playQueue": [
    { "id": "trk-17", "title": "Signal" },
    { "id": "trk-04", "title": "Platform" },
    { "id": "trk-17", "title": "Signal" }
  ],
  "licensedTerritories": ["DE", "NL", "BE"],
  "deviceVolume": {
    "kitchen": 35,
    "headphones": 62
  }
}

The repeated track is deliberate. A queue may play the same track twice, and its position is meaningful. Reordering playQueue changes the instance’s meaning.

Repeating "DE" in licensedTerritories would be invalid. A set is encoded as a JSON array, but all elements must be unique and their order carries no meaning. A processor may therefore map it to a language-level set rather than a list.

The keys under deviceVolume are not declared property names. New devices appear at runtime, so this is a map; every key is a JSON string and every value must satisfy the uint8 schema. values, not items, declares that value type.

How other schema systems spell it

JSON Schema can express much of this with validation vocabulary. Arrays are ordered, uniqueItems can prohibit duplicates, and additionalProperties can constrain dictionary values. A tool must interpret the combination of keywords to recover the intended collection model. JSON Structure declares that model as the type.

The Avro specification defines array and map schemas but no set schema. When an Avro array represents set-like data, uniqueness is an application-level rule. JSON Structure puts that rule into the declared set type.

XML Schema has model groups, repeated elements, and identity constraints. It has no built-in map datatype. A schema can model map-shaped data with repeated entry elements containing keys and values.

Start with the allowed operations

Choose the type from the operations the contract permits, not from the JSON syntax used to carry it.

  • Choose array if a consumer may address an element by position, insert it at a position, or retain the same value more than once. Those are queue operations.
  • Choose set if a consumer may test membership, add a value, or remove a value, but may not assign that value a position. Those are territory-list operations.
  • Choose map if a consumer may get, put, or remove a value by a key supplied as data. Those are per-device-setting operations.

An object answers a different question. Its property names are part of the type, so consumers work with declared fields such as title. A map’s keys are part of the instance, so consumers work with entries such as "kitchen" and "headphones" that appear at runtime.

The current contents do not decide the type. A queue containing no duplicate tracks is still an array. A territory set happens to have an array-shaped JSON encoding, but consumers cannot use its element positions as part of the contract. A device map may be emitted in a stable order by one implementation, but consumers cannot rely on that order. If the contract permits positional operations, model an array.