OpenAI Comparison

This page outlines practical differences between dotjson and OpenAI’s Structured Outputs with JSON Schema, focusing on what you can do in dotjson that is limited or disallowed in OpenAI, and why those capabilities matter in real applications.

Key Differences at a glance

  • allOf: Supported in dotjson for composing/merging constraints from shared definitions.
  • Optional fields: Supported in dotjson. In OpenAI, fields typically need to be listed as required (no true optionals), which forces placeholders or post-processing.
  • Root anyOf (unions): Supported in dotjson so the entire response can be one of several schema variants.
  • Arbitrary JSON fields: Supported in dotjson for pass‑through or flexible metadata blocks.
  • additionalProperties: Defaults to false, but true is allowed. Enables dynamic keys (maps).
  • Multi‑type via type: [ ... ]: Shorthand unions like "type": ["string", "number"] for properties and array items.
  • Not supported in dotjson: multipleOf, exclusiveMinimum, exclusiveMaximum (use minimum/maximum instead).

What you can do with dotjson

Optional fields

Real-world responses are often partially known or progressive. Optional properties let you express “include it when available” without workarounds.

For instance, a summary object may include an optional citations array only when the model finds sources. No dummy empty arrays or re-prompts required.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "title": { "type": "string", "minLength": 3 },
    "citations": {
      "type": "array",
      "items": { "type": "string", "format": "uri" }
    }
  },
  "required": ["title"],
  "additionalProperties": false
}

anyOf

Union types at the root are useful when the output can be fundamentally different shapes.

Take the example of an entity extraction task. The result can be either a Person or an Organization record depending on the text. With root anyOf, you avoid embedding a type discriminator and nested payload.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "anyOf": [
    { "$ref": "#/$defs/Person" },
    { "$ref": "#/$defs/Organization" }
  ],
  "$defs": {
    "Person": {
      "type": "object",
      "required": ["kind", "name"],
      "properties": {
        "kind": { "const": "person" },
        "name": { "type": "string" },
        "email": { "type": "string", "format": "email" }
      },
      "additionalProperties": false
    },
    "Organization": {
      "type": "object",
      "required": ["kind", "legal_name"],
      "properties": {
        "kind": { "const": "org" },
        "legal_name": { "type": "string" },
        "domain": { "type": "string", "format": "hostname" }
      },
      "additionalProperties": false
    }
  }
}

Multi‑type shorthand with type: ["string", "number"]

dotjson supports the JSON Schema shorthand for simple unions using a type array. This is convenient when you don’t need full anyOf branches.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["value", "values"],
  "properties": {
    "value": { "type": ["string", "number"] },
    "values": {
      "type": "array",
      "items": { "type": ["string", "number"] }
    }
  },
  "additionalProperties": false
}

Valid outputs include:

{ "value": "free", "values": ["a", 1, "b"] }
{ "value": 9.99, "values": [1, 2, 3] }

allOf composition

Combine a base contract with overlays for reuse and clarity.

For instance to generate addresses you can define a BaseAddress and country-specific overlays. In the following example we apply a USAddress overlay that adds state rules with allOf. You keep one canonical base and thin, region‑specific layers.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$defs": {
    "BaseAddress": {
      "type": "object",
      "required": ["line1", "city", "postal_code"],
      "properties": {
        "line1": { "type": "string" },
        "line2": { "type": "string" },
        "city": { "type": "string" },
        "postal_code": { "type": "string" }
      },
      "additionalProperties": false
    },
    "USOverlay": {
      "type": "object",
      "required": ["state"],
      "properties": {
        "state": { "type": "string", "minLength": 2, "maxLength": 2 }
      }
    }
  },
  "allOf": [
    { "$ref": "#/$defs/BaseAddress" },
    { "$ref": "#/$defs/USOverlay" }
  ]
}

Arbitrary JSON fields

Sometimes you need a “bag of keys” that the model can fill with arbitrary data.

You can use it to represent pass‑through metadata: a metadata object where keys are not predetermined (e.g., { "sourceA": {...}, "detector": {"score": 0.92} }). The key is to opt in to dynamic keys with additionalProperties.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["id", "metadata"],
  "properties": {
    "id": { "type": "string", "format": "uuid" },
    "metadata": {
      "type": "object",
      "additionalProperties": {
        "type": "object"
      }
    }
  },
  "additionalProperties": false
}

Why additionalProperties: true (or a schema) is different from { "type": "object" }:

  • In dotjson, unspecified properties are not generated by default (effectively locked down). A field like "metadata": { "type": "object" } will typically yield {} unless you enumerate keys.
  • Setting additionalProperties explicitly allows dynamic keys and lets you constrain their values:
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["metrics"],
  "properties": {
    "metrics": {
      "type": "object",
      "additionalProperties": {
        "type": "number",
        "minimum": 0,
        "maximum": 1
      }
    }
  },
  "additionalProperties": false
}

You can also use it, for instance, to represent kwargs in a tool call.

What you cannot do with dotjson

  • multipleOf: Not supported. You can use string patterns instead, if feasible.
  • exclusiveMinimum / exclusiveMaximum: Not supported.

If you have a concrete use case that needs one of these, please contact us. We prioritize based on real workflows.

Size & Complexity Limits

dotjson does not impose limits on nesting depth, number of properties, or enum size. You can model deeper, richer structures when needed.

additionalProperties Default

OpenAI commonly requires additionalProperties=false to prevent stray fields. In dotjson, the default is effectively “locked down”: if you don’t specify extra properties, they won’t be generated. This matches how most teams think about contracts for generation—only return what you asked for.