summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/errors.rst9
-rw-r--r--jsonschema/exceptions.py10
-rw-r--r--jsonschema/tests/test_validators.py40
3 files changed, 58 insertions, 1 deletions
diff --git a/docs/errors.rst b/docs/errors.rst
index d382702..15f751b 100644
--- a/docs/errors.rst
+++ b/docs/errors.rst
@@ -16,7 +16,9 @@ raised or returned, depending on which method or function is used.
=============== ================= ========================
`message` `context` `instance`
- `cause` `path`
+ `cause` `json_path`
+
+ `path`
`schema`
@@ -79,6 +81,11 @@ raised or returned, depending on which method or function is used.
`instance`\). The deque can be empty if the error happened
at the root of the instance.
+ .. attribute:: json_path
+
+ A `JSON path <https://goessner.net/articles/JsonPath/index.html>`
+ to the offending element within the instance.
+
.. attribute:: path
Same as `relative_path`.
diff --git a/jsonschema/exceptions.py b/jsonschema/exceptions.py
index 691dcff..deae872 100644
--- a/jsonschema/exceptions.py
+++ b/jsonschema/exceptions.py
@@ -118,6 +118,16 @@ class _Error(Exception):
path.extendleft(reversed(parent.absolute_schema_path))
return path
+ @property
+ def json_path(self):
+ path = '$'
+ for elem in self.absolute_path:
+ if isinstance(elem, int):
+ path += '[' + str(elem) + ']'
+ else:
+ path += '.' + elem
+ return path
+
def _set(self, **kwargs):
for k, v in iteritems(kwargs):
if getattr(self, k) is _unset:
diff --git a/jsonschema/tests/test_validators.py b/jsonschema/tests/test_validators.py
index 07be4f0..abca2a7 100644
--- a/jsonschema/tests/test_validators.py
+++ b/jsonschema/tests/test_validators.py
@@ -630,6 +630,7 @@ class TestValidationErrorDetails(TestCase):
self.assertEqual(e.path, deque([]))
self.assertEqual(e.relative_path, deque([]))
self.assertEqual(e.absolute_path, deque([]))
+ self.assertEqual(e.json_path, '$')
self.assertEqual(e.schema_path, deque(["anyOf"]))
self.assertEqual(e.relative_schema_path, deque(["anyOf"]))
@@ -648,6 +649,7 @@ class TestValidationErrorDetails(TestCase):
self.assertEqual(e1.path, deque([]))
self.assertEqual(e1.absolute_path, deque([]))
self.assertEqual(e1.relative_path, deque([]))
+ self.assertEqual(e1.json_path, '$')
self.assertEqual(e1.schema_path, deque([0, "minimum"]))
self.assertEqual(e1.relative_schema_path, deque([0, "minimum"]))
@@ -666,6 +668,7 @@ class TestValidationErrorDetails(TestCase):
self.assertEqual(e2.path, deque([]))
self.assertEqual(e2.relative_path, deque([]))
self.assertEqual(e2.absolute_path, deque([]))
+ self.assertEqual(e2.json_path, '$')
self.assertEqual(e2.schema_path, deque([1, "type"]))
self.assertEqual(e2.relative_schema_path, deque([1, "type"]))
@@ -699,6 +702,7 @@ class TestValidationErrorDetails(TestCase):
self.assertEqual(e.path, deque([]))
self.assertEqual(e.relative_path, deque([]))
self.assertEqual(e.absolute_path, deque([]))
+ self.assertEqual(e.json_path, '$')
self.assertEqual(e.schema_path, deque(["type"]))
self.assertEqual(e.relative_schema_path, deque(["type"]))
@@ -717,6 +721,7 @@ class TestValidationErrorDetails(TestCase):
self.assertEqual(e1.path, deque([]))
self.assertEqual(e1.relative_path, deque([]))
self.assertEqual(e1.absolute_path, deque([]))
+ self.assertEqual(e1.json_path, '$')
self.assertEqual(e1.schema_path, deque([0, "type"]))
self.assertEqual(e1.relative_schema_path, deque([0, "type"]))
@@ -733,6 +738,7 @@ class TestValidationErrorDetails(TestCase):
self.assertEqual(e2.path, deque(["foo"]))
self.assertEqual(e2.relative_path, deque(["foo"]))
self.assertEqual(e2.absolute_path, deque(["foo"]))
+ self.assertEqual(e2.json_path, '$.foo')
self.assertEqual(
e2.schema_path, deque([1, "properties", "foo", "enum"]),
@@ -776,6 +782,11 @@ class TestValidationErrorDetails(TestCase):
self.assertEqual(e3.absolute_path, deque(["baz"]))
self.assertEqual(e4.absolute_path, deque(["foo"]))
+ self.assertEqual(e1.json_path, '$.bar')
+ self.assertEqual(e2.json_path, '$.baz')
+ self.assertEqual(e3.json_path, '$.baz')
+ self.assertEqual(e4.json_path, '$.foo')
+
self.assertEqual(e1.validator, "minItems")
self.assertEqual(e2.validator, "enum")
self.assertEqual(e3.validator, "maximum")
@@ -811,6 +822,13 @@ class TestValidationErrorDetails(TestCase):
self.assertEqual(e5.path, deque([1, "bar", "baz"]))
self.assertEqual(e6.path, deque([1, "foo"]))
+ self.assertEqual(e1.json_path, '$')
+ self.assertEqual(e2.json_path, '$[0]')
+ self.assertEqual(e3.json_path, '$[1].bar')
+ self.assertEqual(e4.json_path, '$[1].bar.bar')
+ self.assertEqual(e5.json_path, '$[1].bar.baz')
+ self.assertEqual(e6.json_path, '$[1].foo')
+
self.assertEqual(e1.schema_path, deque(["type"]))
self.assertEqual(e2.schema_path, deque(["items", "type"]))
self.assertEqual(
@@ -886,6 +904,7 @@ class TestValidationErrorDetails(TestCase):
self.assertEqual(
e.absolute_schema_path, deque(["properties", "root", "anyOf"]),
)
+ self.assertEqual(e.json_path, '$.root')
e1, = e.context
self.assertEqual(e1.absolute_path, deque(["root", "children", "a"]))
@@ -904,6 +923,7 @@ class TestValidationErrorDetails(TestCase):
],
),
)
+ self.assertEqual(e1.json_path, '$.root.children.a')
e2, = e1.context
self.assertEqual(
@@ -932,6 +952,7 @@ class TestValidationErrorDetails(TestCase):
],
),
)
+ self.assertEqual(e2.json_path, '$.root.children.a.children.ab')
def test_additionalProperties(self):
instance = {"bar": "bar", "foo": 2}
@@ -944,6 +965,9 @@ class TestValidationErrorDetails(TestCase):
self.assertEqual(e1.path, deque(["bar"]))
self.assertEqual(e2.path, deque(["foo"]))
+ self.assertEqual(e1.json_path, '$.bar')
+ self.assertEqual(e2.json_path, '$.foo')
+
self.assertEqual(e1.validator, "type")
self.assertEqual(e2.validator, "minimum")
@@ -963,6 +987,9 @@ class TestValidationErrorDetails(TestCase):
self.assertEqual(e1.path, deque(["bar"]))
self.assertEqual(e2.path, deque(["foo"]))
+ self.assertEqual(e1.json_path, '$.bar')
+ self.assertEqual(e2.json_path, '$.foo')
+
self.assertEqual(e1.validator, "type")
self.assertEqual(e2.validator, "minimum")
@@ -980,6 +1007,9 @@ class TestValidationErrorDetails(TestCase):
self.assertEqual(e1.path, deque([0]))
self.assertEqual(e2.path, deque([1]))
+ self.assertEqual(e1.json_path, '$[0]')
+ self.assertEqual(e2.json_path, '$[1]')
+
self.assertEqual(e1.validator, "type")
self.assertEqual(e2.validator, "minimum")
@@ -997,6 +1027,9 @@ class TestValidationErrorDetails(TestCase):
self.assertEqual(e1.path, deque([1]))
self.assertEqual(e2.path, deque([2]))
+ self.assertEqual(e1.json_path, '$[1]')
+ self.assertEqual(e2.json_path, '$[2]')
+
self.assertEqual(e1.validator, "type")
self.assertEqual(e2.validator, "minimum")
@@ -1013,6 +1046,7 @@ class TestValidationErrorDetails(TestCase):
"%r is not allowed for %r" % ({"const": "foo"}, "foo"),
)
self.assertEqual(error.path, deque([]))
+ self.assertEqual(error.json_path, '$')
self.assertEqual(error.schema_path, deque(["propertyNames", "not"]))
def test_if_then(self):
@@ -1027,6 +1061,7 @@ class TestValidationErrorDetails(TestCase):
self.assertEqual(error.validator, "const")
self.assertEqual(error.message, "13 was expected")
self.assertEqual(error.path, deque([]))
+ self.assertEqual(error.json_path, '$')
self.assertEqual(error.schema_path, deque(["if", "then", "const"]))
def test_if_else(self):
@@ -1041,6 +1076,7 @@ class TestValidationErrorDetails(TestCase):
self.assertEqual(error.validator, "const")
self.assertEqual(error.message, "13 was expected")
self.assertEqual(error.path, deque([]))
+ self.assertEqual(error.json_path, '$')
self.assertEqual(error.schema_path, deque(["if", "else", "const"]))
def test_boolean_schema_False(self):
@@ -1055,6 +1091,7 @@ class TestValidationErrorDetails(TestCase):
error.instance,
error.schema,
error.schema_path,
+ error.json_path,
),
(
"False schema does not allow 12",
@@ -1063,6 +1100,7 @@ class TestValidationErrorDetails(TestCase):
12,
False,
deque([]),
+ '$',
),
)
@@ -1083,6 +1121,7 @@ class TestValidationErrorDetails(TestCase):
error.absolute_path,
error.schema,
error.schema_path,
+ error.json_path,
),
(
"'notAnInteger' is not of type 'integer'",
@@ -1092,6 +1131,7 @@ class TestValidationErrorDetails(TestCase):
deque(["foo"]),
{"type": "integer"},
deque(["additionalProperties", "type"]),
+ '$.foo',
),
)