Specification scope: Core with the Import companion specification.

Two imported schemas can both define Address, and each can refer to its own supporting definitions. Copying those declarations without namespaces and rewritten pointers would make local type identity depend on import order.

An import processor prevents that ambiguity. It places copied types in a chosen namespace, rewrites their internal pointers, and gives local declarations deterministic precedence over imported names.

Two source documents

The first document publishes a root type and one supporting definition at https://schemas.example.com/people.json:

{
  "$schema": "https://json-structure.org/meta/core/v0/#",
  "$id": "https://schemas.example.com/people.json",
  "name": "Person",
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "address": { "type": { "$ref": "#/definitions/Address" } }
  },
  "required": ["name", "address"],
  "definitions": {
    "Address": {
      "type": "object",
      "properties": {
        "city": { "type": "string" }
      },
      "required": ["city"]
    }
  }
}

The second is a definition library at https://schemas.example.com/common.json:

{
  "$schema": "https://json-structure.org/meta/core/v0/#",
  "$id": "https://schemas.example.com/common.json",
  "name": "CommonDocument",
  "type": "object",
  "definitions": {
    "AuditStamp": {
      "type": "object",
      "properties": {
        "createdBy": { "type": "string" },
        "createdAt": { "type": "datetime" }
      },
      "required": ["createdBy", "createdAt"]
    }
  }
}

One consumer, two import modes

The consumer imports the complete people document and only the definitions from the common document:

{
  "$schema": "https://json-structure.org/meta/extended/v0/#",
  "$id": "https://schemas.example.com/customer-record.json",
  "$uses": ["JSONStructureImport"],
  "name": "CustomerRecord",
  "type": "object",
  "properties": {
    "customer": {
      "type": { "$ref": "#/definitions/People/Person" }
    },
    "audit": {
      "type": { "$ref": "#/definitions/Common/AuditStamp" }
    }
  },
  "required": ["customer", "audit"],
  "definitions": {
    "People": {
      "$import": "https://schemas.example.com/people.json",
      "Address": {
        "type": "object",
        "properties": {
          "city": { "type": "string" },
          "country": { "type": "string" }
        },
        "required": ["city", "country"]
      }
    },
    "Common": {
      "$importdefs": "https://schemas.example.com/common.json"
    }
  }
}

The extended meta-schema offers JSONStructureImport and enables it for its own root import. The import draft, unlike the validation and conditional drafts, introduces no competing $uses name; it makes the extension available through the extended meta-schema. The explicit $uses above names the repository feature so a reader can see the dependency at the point of use.

A matching instance is ordinary JSON:

{
  "customer": {
    "name": "Ada Lovelace",
    "address": {
      "city": "London",
      "country": "GB"
    }
  },
  "audit": {
    "createdBy": "migration-7",
    "createdAt": "2026-11-09T10:30:00Z"
  }
}

$import copies the external root type as People/Person and copies its definitions. $importdefs copies only the external definitions, so Common/AuditStamp exists but Common/CommonDocument does not.

Placement supplies the namespace. An import at the schema root, or directly as definitions.$import, targets the root namespace. An import inside definitions.People targets People. Nested namespace objects can place it deeper still.

Internal pointers stay internal

The source Person points to #/definitions/Address. This value is a JSON Pointer into the source schema. During import, the processor prefixes it so it resolves inside People, not against a consumer-level Address. The rewrite also applies recursively through nested imports and to $ref, $extends, and $addins pointers.

Shadowing comes next. The local People/Address replaces the imported type of the same name entirely; there is no merge and no route back to the replaced version. Consequently the imported Person.address resolves to the local replacement requiring both city and country.

That precedence is deterministic, but blunt. Put both versions under different namespaces when both must survive.

URI resolution is not package lookup

The import draft requires absolute URI values and RFC 3986/RFC 3987 resolution. It defines no npm-style package search, filesystem fallback, registry alias, or version selection. Processors must fetch and validate the target as a schema; the draft also requires safeguards against circular or excessively deep chains.

Repository samples sometimes use relative paths for local development. Those are useful fixtures, but they conflict with the draft’s absolute-URI requirement. The complete documents above follow the draft.