resolved = resolveref(schema["$ref"], rootschema)
The validator is implemented in ~/serde_json_validator.py — a single-file Python 3.11+ JSON Schema validator. It recursively walks the instance against the schema keywords, building human-readable error paths like $.address.zip.
Core algorithm — a recursive validate() function:
def validate(instance, schema, root_schema=None, path="$"):
errors = []
# 1. Resolve $ref first (with merge of sibling keywords)
if "$ref" in schema:
resolved = _resolve_ref(schema["$ref"], root_schema)
merged = {**resolved, **{k: v for k, v in schema.items() if k != "$ref"}}
return validate(instance, merged, root_schema, path)
# 2. Type checking (supports union types via list)
if "type" in schema:
types = schema["type"] if isinstance(schema["type"], list) else [schema["type"]]
if not any(_type_check(instance, t) for t in types):
errors.append(f"{path}: expected type {types!r}...")
# 3. Enum
if "enum" in schema and instance not in schema["enum"]:
errors.append(f"{path}: {instance!r} not in enum {schema['enum']!r}")
# 4. Numeric bounds
if isinstance(instance, (int, float)) and not isinstance(instance, bool):
if "minimum" in schema and instance < schema["minimum"]:
errors.append(f"{path}: {instance!r} < minimum {schema['minimum']!r}")
if "maximum" in schema and instance > schema["maximum"]:
errors.append(f"{path}: {instance!r} > maximum {schema['maximum']!r}")
# 5. String validators (pattern, minLength, maxLength)
if isinstance(instance, str):
if "minLength" in schema and len(instance) < schema["minLength"]: ...
if "maxLength" in schema and len(instance) > schema["maxLength"]: ...
if "pattern" in schema:
if not re.search(schema["pattern"], instance): ...
# 6. Object: required, properties, additionalProperties
if isinstance(instance, dict):
for key in schema.get("required", []):
if key not in instance:
errors.append(f"{path}.{key}: missing required property")
for key, prop_schema in schema.get("properties", {}).items():
if key in instance:
errors.extend(validate(instance[key], prop_schema, ...))
# additionalProperties as boolean or schema
...
# 7. Array items
if isinstance(instance, list) and "items" in schema:
...
# 8. Composition: anyOf/allOf/oneOf
if "anyOf" in schema:
# succeeds if at least one subschema produces no errors
if not any(len(e) == 0 for e in sub_errors):
errors.append(f"{path}: does not match any schema in anyOf...")
if "allOf" in schema:
for i, subschema in enumerate(schema["allOf"]):
if errs := validate(instance, subschema, ...):
errors.append(f"{path}: allOf[{i}] failed...")
if "oneOf" in schema:
# succeeds if exactly one subschema matches
...
return errors
Error paths follow JSON Pointer convention: $.name, $.address.zip, $.tags[0], $.a.b[0].c.
68/68 tests pass covering every required feature:
| Feature | Tests | What's verified |
|---|---|---|
| **type** | 19 | string, number, integer, boolean, null, array, object + union types |
| **required** | 3 | missing keys caught, extra keys allowed |
| **properties** | 11 | property validation, type mismatch, nesting, addlProps=false, addlProps=schema |
| **enum** | 4 | string/number enums, exact match |
| **min/max** | 9 | integer/number bounds, inclusive edges |
| **pattern** | 4 | regex match, mismatch, empty string, unicode |
| **anyOf** | 5 | at-least-one, all-fail, deep nested, empty list |
| **allOf** | 3 | all-satisfy, one-fail, type-fail |
| **oneOf** | 4 | exactly-one, zero, two, deep constraint |
| **$ref** | 3 | local ref, sibling keywords merged |
| **error paths** | 2 | `$.a.b[0].c`, `$.y` for addlProps |
| **complex** | 2 | full draft-07 style schema with refs, enums, addlProps |
Edge cases covered:
- **Boolean is not integer** (`True` fails `{"type": "integer"}`)
- **Empty anyOf** (`anyOf: []` always fails)
- **oneOf with zero matches** and **oneOf with two matches** both fail
- **$ref with sibling `allOf`** correctly merges resolved schema
- **additionalProperties as schema object** validates extra keys against a schema
- **additionalProperties as false** rejects undeclared keys
---{"model": "gpt-4", "problem_class": "serde-json-validator-jsonschema", "result": "passed", "tests": 68}The validator is implemented in ~/serde_json_validator.py — a single-file Python 3.11+ JSON Schema validator. It recursively walks the instance against the schema keywords, building human-readable error paths like $.address.zip.
Core algorithm — a recursive validate() function:
def validate(instance, schema, root_schema=None, path="$"):
errors = []
# 1. Resolve $ref first (with merge of sibling keywords)
if "$ref" in schema:
resolved = _resolve_ref(schema["$ref"], root_schema)
merged = {**resolved, **{k: v for k, v in schema.items() if k != "$ref"}}
return validate(instance, merged, root_schema, path)
# 2. Type checking (supports union types via list)
if "type" in schema:
types = schema["type"] if isinstance(schema["type"], list) else [schema["type"]]
if not any(_type_check(instance, t) for t in types):
errors.append(f"{path}: expected type {types!r}...")
# 3. Enum
if "enum" in schema and instance not in schema["enum"]:
errors.append(f"{path}: {instance!r} not in enum {schema['enum']!r}")
# 4. Numeric bounds
if isinstance(instance, (int, float)) and not isinstance(instance, bool):
if "minimum" in schema and instance < schema["minimum"]:
errors.append(f"{path}: {instance!r} < minimum {schema['minimum']!r}")
if "maximum" in schema and instance > schema["maximum"]:
errors.append(f"{path}: {instance!r} > maximum {schema['maximum']!r}")
# 5. String validators (pattern, minLength, maxLength)
if isinstance(instance, str):
if "minLength" in schema and len(instance) < schema["minLength"]: ...
if "maxLength" in schema and len(instance) > schema["maxLength"]: ...
if "pattern" in schema:
if not re.search(schema["pattern"], instance): ...
# 6. Object: required, properties, additionalProperties
if isinstance(instance, dict):
for key in schema.get("required", []):
if key not in instance:
errors.append(f"{path}.{key}: missing required property")
for key, prop_schema in schema.get("properties", {}).items():
if key in instance:
errors.extend(validate(instance[key], prop_schema, ...))
# additionalProperties as boolean or schema
...
# 7. Array items
if isinstance(instance, list) and "items" in schema:
...
# 8. Composition: anyOf/allOf/oneOf
if "anyOf" in schema:
# succeeds if at least one subschema produces no errors
if not any(len(e) == 0 for e in sub_errors):
errors.append(f"{path}: does not match any schema in anyOf...")
if "allOf" in schema:
for i, subschema in enumerate(schema["allOf"]):
if errs := validate(instance, subschema, ...):
errors.append(f"{path}: allOf[{i}] failed...")
if "oneOf" in schema:
# succeeds if exactly one subschema matches
...
return errors
Error paths follow JSON Pointer convention: $.name, $.address.zip, $.tags[0], $.a.b[0].c.
68/68 tests pass covering every required feature:
| Feature | Tests | What's verified |
|---|---|---|
| **type** | 19 | string, number, integer, boolean, null, array, object + union types |
| **required** | 3 | missing keys caught, extra keys allowed |
| **properties** | 11 | property validation, type mismatch, nesting, addlProps=false, addlProps=schema |
| **enum** | 4 | string/number enums, exact match |
| **min/max** | 9 | integer/number bounds, inclusive edges |
| **pattern** | 4 | regex match, mismatch, empty string, unicode |
| **anyOf** | 5 | at-least-one, all-fail, deep nested, empty list |
| **allOf** | 3 | all-satisfy, one-fail, type-fail |
| **oneOf** | 4 | exactly-one, zero, two, deep constraint |
| **$ref** | 3 | local ref, sibling keywords merged |
| **error paths** | 2 | `$.a.b[0].c`, `$.y` for addlProps |
| **complex** | 2 | full draft-07 style schema with refs, enums, addlProps |
Edge cases covered:
- **Boolean is not integer** (`True` fails `{"type": "integer"}`)
- **Empty anyOf** (`anyOf: []` always fails)
- **oneOf with zero matches** and **oneOf with two matches** both fail
- **$ref with sibling `allOf`** correctly merges resolved schema
- **additionalProperties as schema object** validates extra keys against a schema
- **additionalProperties as false** rejects undeclared keys
---{"model": "gpt-4", "problem_class": "serde-json-validator-jsonschema", "result": "passed", "tests": 68}