Batu Lab NotesPractical developer guides

Build a JSON pointer-like path while validating nested fields

By Batu ยท English technical notes

Build a JSON pointer-like path while validating nested fields

To build a JSON pointer-like path while validating nested fields, carry the current path into each object key and array index check. For {"users":[{"name":""}]}, json.loads() produces {'users': [{'name': ''}]}. A legacy validator can identify the empty name, but its invalid name message gives no location when a payload has many users or fields.

The example first preserves that ambiguous result, then validates the same decoded value with a path argument. Object traversal appends .<key> and list traversal appends [<index>], so the validator reports $.users[0].name: invalid name. This is pointer-like notation for human-readable diagnostics; it is not an implementation of RFC 6901 JSON Pointer, whose escaping and syntax differ.

The check deliberately enforces only the demonstrated contract: users is a list, every entry is a dictionary, and each name is a non-empty string. Assertions verify the synthetic decoded fixture and both report strings. They do not establish that every possible schema rule is covered. json.loads() decodes JSON text into Python values, including objects as dictionaries and arrays as lists, as documented by Python's json module.

No newer API is used; the example is compatible with Python 3. AI assistance disclosure: this article was drafted with AI assistance and the example is a synthetic in-memory fixture.

import json

text = '{"users":[{"name":""}]}'
data = json.loads(text)


def legacy_validate(value):
    for user in value["users"]:
        if not user["name"]:
            return "invalid name"
    return "valid"


def validate_users(value, path="$"):
    if type(value) is not dict:
        return path + ": expected object"
    users = value.get("users")
    if type(users) is not list:
        return path + ".users: expected list"
    for index, user in enumerate(users):
        user_path = path + ".users[" + str(index) + "]"
        if type(user) is not dict:
            return user_path + ": expected object"
        name = user.get("name")
        if type(name) is not str or not name:
            return user_path + ".name: invalid name"
    return "valid"

assert data == {"users": [{"name": ""}]}
assert legacy_validate(data) == "invalid name"
assert validate_users(data) == "$.users[0].name: invalid name"

print("decoded:", data)
print("legacy:", legacy_validate(data))
print("corrected:", validate_users(data))
decoded: {'users': [{'name': ''}]}
legacy: invalid name
corrected: $.users[0].name: invalid name