python-jsonschema-objects
python-jsonschema-objects copied to clipboard
Chained references are not resolved correctly
Describe the bug
I only recently tried to switch to version 0.5.5 (from 0.4.6) and noticed that apparently relative $ref
s in a referenced document are not resolved correctly anymore with the new referencing
lib.
So, when I reference a definition d1 within a a schema schema-a from a schema-b, and d1 references another definition d2 within the very same schema-a using a local JSON pointer, the resolution of d2 fails as it searches in schema-b. With version 0.4.6 this exact use case worked using the legacy RefResolver
.
Example Schema and code
import python_jsonschema_objects as pjs
import referencing
SCHEMA_A = {
'$id': 'schema-a',
'$schema': 'http://json-schema.org/draft-04/schema#',
'title': "Schema A",
'definitions': {
'myint': {
'type': 'integer',
'minimum': 42
},
'myintarray': {
'type': 'array',
'items': {
'$ref': '#/definitions/myint', # using 'schema-a#/definitions/myint' would work
},
},
'myintref': {
'$ref': '#/definitions/myint', # using 'schema-a#/definitions/myint' would work
},
},
'type': 'object',
'properties': {
'theint': {
'$ref': '#/definitions/myint', # using 'schema-a#/definitions/myint' would work
},
},
}
SCHEMA_B = {
'$id': 'schema-b',
'$schema': 'http://json-schema.org/draft-04/schema#',
'title': "Schema B",
'type': 'object',
'definitions': {
'myintref': {
'$ref': 'schema-a#/definitions/myintref',
},
},
'properties': {
# all three properties cause the failure
'obja': {
'$ref': 'schema-a',
},
'theintarray': {
'$ref': 'schema-a#/definitions/myintarray',
},
'theintref': {
'$ref': '#/definitions/myintref',
},
},
}
registry = referencing.Registry().with_resources([
('schema-a', referencing.Resource.from_contents(SCHEMA_A)),
('schema-b', referencing.Resource.from_contents(SCHEMA_B)),
])
# works fine
builder_a = pjs.ObjectBuilder(SCHEMA_A, registry=registry)
namespace_a = builder_a.build_classes(named_only=False)
# fails
builder_b = pjs.ObjectBuilder(SCHEMA_B, registry=registry)
namespace_b = builder_b.build_classes(named_only=False) # referencing.exceptions.PointerToNowhere: '/definitions/myint' does not exist within SCHEMA_B
b = namespace_b.SchemaB()
b.obja = {'theint': 42}
b.theintarray = [42, 43, 44]
b.theintref = 42
print(b.for_json())
The exception throws by build_classes()
actually references SCHEMA_B
, although the given pointer /definitions/myint
is only ever used in SCHEMA_A
.
Expected behavior I would expect that local JSON pointers are resolved within the current document not within any referencing document.
Workaround
Make all $ref
s absolute.
Fix
I believe that there needs to be a new ClassBuilder
instance with an adapted resolver when recursing in ClassBuilder.resolve_type()
. My quick and dirty patch seems to work and the above code prints {'obja': {'theint': 42}, 'theintarray': [42, 43, 44], 'theintref': 42}
:
--- a/python_jsonschema_objects/classbuilder.py
+++ b/python_jsonschema_objects/classbuilder.py
@@ -492,7 +492,8 @@
)
)
resolved = self.resolver.lookup(ref)
- self.resolved[uri] = self.construct(uri, resolved.contents, (ProtocolBase,))
+ sub_cb = ClassBuilder(referencing._core.Resolver(base_uri=uri.split('#')[0], registry=self.resolver._registry))
+ self.resolved[uri] = sub_cb.construct(uri, resolved.contents, (ProtocolBase,))
return self.resolved[uri]
def construct(self, uri, *args, **kw):