Hyperlinkv0.8.0-beta.28

JsonSchema

JsonSchema.fromSchemaDraft07functioneffect/JsonSchema.ts:250
(js: JsonSchema): Document<"draft-2020-12">

Parses a raw Draft-07 JSON Schema into a Document<"draft-2020-12">.

When to use

Use when you have a raw JSON Schema object that follows Draft-07 conventions and need the canonical Draft-2020-12 document representation.

Details

This converts Draft-07 tuple syntax (items as array plus additionalItems) to Draft-2020-12 form (prefixItems plus items), rewrites #/definitions/... refs to #/$defs/..., and extracts root-level definitions into the definitions field.

Gotchas

Unsupported keywords, such as if/then/else and $id, are dropped.

Example (Parsing a Draft-07 schema)

import { JsonSchema } from "effect"

const raw: JsonSchema.JsonSchema = {
  type: "object",
  properties: {
    tags: {
      type: "array",
      items: { type: "string" }
    }
  }
}

const doc = JsonSchema.fromSchemaDraft07(raw)
console.log(doc.dialect) // "draft-2020-12"
console.log(doc.schema.properties) // { tags: { type: "array", items: { type: "string" } } }
Source effect/JsonSchema.ts:250107 lines
export function fromSchemaDraft07(js: JsonSchema): Document<"draft-2020-12"> {
  let definitions: Definitions | undefined

  const schema = walk(js, true) as JsonSchema
  return {
    dialect: "draft-2020-12",
    schema,
    definitions: definitions ?? {}
  }

  function walk(node: unknown, isRoot: boolean): unknown {
    if (Array.isArray(node)) return node.map((v) => walk(v, false))
    if (!Predicate.isObject(node)) return node

    const out: Record<string, unknown> = {}

    let prefixItems: unknown = undefined
    let additionalItems: unknown = undefined

    for (const k of Object.keys(node)) {
      const v = node[k]

      switch (k) {
        case "$ref":
          out.$ref = typeof v === "string" ? v.replace(RE_DEFINITIONS, "#/$defs") : v
          break

        case "definitions": {
          const mapped = walk_object(v, walk)
          if (isRoot) {
            definitions = mapped as Definitions | undefined
          } else {
            out.definitions = mapped ?? v
          }
          break
        }

        case "items":
          prefixItems = v
          break
        case "additionalItems":
          additionalItems = v
          break

        case "properties":
        case "patternProperties": {
          const mapped = walk_object(v, walk)
          out[k] = mapped ?? v
          break
        }

        case "additionalProperties":
        case "propertyNames":
          out[k] = walk(v, false)
          break

        case "allOf":
        case "anyOf":
        case "oneOf":
          out[k] = Array.isArray(v) ? v.map((x) => walk(x, false)) : v
          break

        case "type":
        case "required":
        case "enum":
        case "const":
        case "title":
        case "description":
        case "default":
        case "examples":
        case "format":
        case "readOnly":
        case "writeOnly":
        case "pattern":
        case "minimum":
        case "maximum":
        case "exclusiveMinimum":
        case "exclusiveMaximum":
        case "minLength":
        case "maxLength":
        case "minItems":
        case "maxItems":
        case "minProperties":
        case "maxProperties":
        case "multipleOf":
        case "uniqueItems":
          out[k] = v
          break

        default:
          break
      }
    }

    // Draft-07 tuples -> 2020-12 tuples
    if (prefixItems !== undefined) {
      if (Array.isArray(prefixItems)) {
        out.prefixItems = prefixItems.map((x) => walk(x, false))
        if (additionalItems !== undefined) out.items = walk(additionalItems, false)
      } else {
        out.items = walk(prefixItems, false)
      }
    }

    return out
  }
}
Referenced by 1 symbols