Skip to content

Commit 0d3860a

Browse files
authored
Merge pull request #5 from json-structure/fix-import-ref-rewriting
Add external_schemas parameter and remove hardcoded test schemas
2 parents a25f982 + 8de45fb commit 0d3860a

File tree

2 files changed

+38
-76
lines changed

2 files changed

+38
-76
lines changed

samples/py/json_structure_instance_validator.py

Lines changed: 19 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,28 @@ class JSONStructureInstanceValidator:
5050
"""
5151
ABSOLUTE_URI_REGEX = re.compile(r'^[a-zA-Z][a-zA-Z0-9+\-.]*://')
5252

53-
def __init__(self, root_schema, allow_import=False, import_map=None, extended=False):
53+
def __init__(self, root_schema, allow_import=False, import_map=None, extended=False, external_schemas=None):
5454
"""
5555
Initializes the validator.
5656
:param root_schema: The JSON Structure (as dict).
5757
:param allow_import: Enables processing of $import/$importdefs.
5858
:param import_map: Dict mapping URIs to local filenames.
59+
:param extended: Enable extended validation features.
60+
:param external_schemas: List of schema dicts to use for resolving imports by $id.
61+
Each schema should have a '$id' field matching the import URI.
5962
"""
6063
self.root_schema = root_schema
6164
self.errors = []
6265
self.allow_import = allow_import
6366
self.import_map = import_map if import_map is not None else {}
6467
self.extended = extended
6568
self.enabled_extensions = set()
69+
# Build lookup for external schemas by $id
70+
self.external_schemas = {}
71+
if external_schemas:
72+
for schema in external_schemas:
73+
if isinstance(schema, dict) and "$id" in schema:
74+
self.external_schemas[schema["$id"]] = schema
6675
# Process $import and $importdefs if enabled. [Metaschema: JSONStructureImport extension constructs]
6776
if self.allow_import:
6877
self._process_imports(self.root_schema, "#")
@@ -1032,50 +1041,23 @@ def _fetch_external_schema(self, uri):
10321041
"""
10331042
Fetches an external schema from a URI.
10341043
[Metaschema: JSONStructureImport extension resolution]
1035-
If the URI is in self.import_map, loads the schema from the specified file.
1036-
Otherwise, uses a simulated lookup.
1044+
Resolution order:
1045+
1. Check external_schemas (pre-loaded schemas matched by $id)
1046+
2. Check import_map (URI to file path mapping)
10371047
"""
1048+
# First check sideloaded schemas by $id
1049+
if uri in self.external_schemas:
1050+
return self.external_schemas[uri]
1051+
# Then check import_map for file paths
10381052
if uri in self.import_map:
10391053
try:
10401054
with open(self.import_map[uri], "r", encoding="utf-8") as f:
10411055
return json.load(f)
10421056
except Exception as e:
10431057
self.errors.append(f"Failed to load imported schema from {self.import_map[uri]}: {e}")
10441058
return None
1045-
SIMULATED_SCHEMAS = {
1046-
"https://example.com/people.json": {
1047-
"$schema": "https://json-structure.org/meta/core/v0/#",
1048-
"$id": "https://example.com/people.json",
1049-
"name": "Person",
1050-
"type": "object",
1051-
"properties": {
1052-
"firstName": {"type": "string"},
1053-
"lastName": {"type": "string"},
1054-
"address": {"$ref": "#/Address"}
1055-
},
1056-
"definitions": {
1057-
"Address": {
1058-
"name": "Address",
1059-
"type": "object",
1060-
"properties": {
1061-
"street": {"type": "string"},
1062-
"city": {"type": "string"}
1063-
}
1064-
}
1065-
}
1066-
},
1067-
"https://example.com/importdefs.json": {
1068-
"$schema": "https://json-structure.org/meta/core/v0/#",
1069-
"$id": "https://example.com/importdefs.json",
1070-
"definitions": {
1071-
"LibraryType": {
1072-
"name": "LibraryType",
1073-
"type": "string"
1074-
}
1075-
}
1076-
}
1077-
}
1078-
return SIMULATED_SCHEMAS.get(uri)
1059+
# URI not found in external_schemas or import_map
1060+
return None
10791061

10801062
def _apply_uses(self, schema, instance):
10811063
"""

samples/py/json_structure_schema_validator.py

Lines changed: 19 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,15 @@ class JSONStructureSchemaCoreValidator:
7272
"JSONStructureConditionalComposition", "JSONStructureValidation"
7373
}
7474

75-
def __init__(self, allow_dollar=False, allow_import=False, import_map=None, extended=False):
75+
def __init__(self, allow_dollar=False, allow_import=False, import_map=None, extended=False, external_schemas=None):
7676
"""
7777
Initializes a validator instance.
7878
:param allow_dollar: Boolean flag to allow '$' in property names.
7979
:param allow_import: Boolean flag to enable processing of $import/$importdefs.
8080
:param import_map: Dictionary mapping URI to local filenames.
8181
:param extended: Boolean flag to enable extended validation features.
82+
:param external_schemas: List of schema dicts to use for resolving imports by $id.
83+
Each schema should have a '$id' field matching the import URI.
8284
"""
8385
self.errors = []
8486
self.doc = None
@@ -87,6 +89,12 @@ def __init__(self, allow_dollar=False, allow_import=False, import_map=None, exte
8789
self.import_map = import_map if import_map is not None else {}
8890
self.extended = extended
8991
self.enabled_extensions = set()
92+
# Build lookup for external schemas by $id
93+
self.external_schemas = {}
94+
if external_schemas:
95+
for schema in external_schemas:
96+
if isinstance(schema, dict) and "$id" in schema:
97+
self.external_schemas[schema["$id"]] = schema
9098
if allow_dollar:
9199
self.identifier_regex = re.compile(r'^[A-Za-z_$][A-Za-z0-9_$]*$')
92100
else:
@@ -289,51 +297,23 @@ def _process_imports(self, obj, path):
289297
def _fetch_external_schema(self, uri):
290298
"""
291299
Fetches an external schema from a URI.
292-
If a mapping is provided and contains the URI, loads from the given file.
293-
Otherwise, uses a simulated lookup.
294-
"""
300+
Resolution order:
301+
1. Check external_schemas (pre-loaded schemas matched by $id)
302+
2. Check import_map (URI to file path mapping)
303+
"""
304+
# First check sideloaded schemas by $id
305+
if uri in self.external_schemas:
306+
return self.external_schemas[uri]
307+
# Then check import_map for file paths
295308
if uri in self.import_map:
296309
try:
297310
with open(self.import_map[uri], "r", encoding="utf-8") as f:
298311
return json.load(f)
299312
except Exception as e:
300313
self._err(f"Failed to load imported schema from {self.import_map[uri]}: {e}", "#/import")
301314
return None
302-
# Simulated external schemas for testing purposes.
303-
EXTERNAL_SCHEMAS = {
304-
"https://example.com/people.json": {
305-
"$schema": "https://json-structure.org/meta/core/v0/#",
306-
"$id": "https://example.com/people.json",
307-
"name": "Person",
308-
"type": "object",
309-
"properties": {
310-
"firstName": {"type": "string"},
311-
"lastName": {"type": "string"},
312-
"address": {"$ref": "#/definitions/Address"}
313-
},
314-
"definitions": {
315-
"Address": {
316-
"name": "Address",
317-
"type": "object",
318-
"properties": {
319-
"street": {"type": "string"},
320-
"city": {"type": "string"}
321-
}
322-
}
323-
}
324-
},
325-
"https://example.com/address.json": {
326-
"$schema": "https://json-structure.org/meta/core/v0/#",
327-
"$id": "https://example.com/address.json",
328-
"name": "Address",
329-
"type": "object",
330-
"properties": {
331-
"street": {"type": "string"},
332-
"city": {"type": "string"}
333-
}
334-
}
335-
}
336-
return EXTERNAL_SCHEMAS.get(uri)
315+
# URI not found in external_schemas or import_map
316+
return None
337317

338318
def _validate_namespace(self, obj, path):
339319
"""

0 commit comments

Comments
 (0)