Search code examples
pythonjsonflaskjinja2traversal

Use of dots inside brackets to reach variable depth JSON in Jinja2 like ['object.subobject']?


It's easy to get a specific variable object from a JSON array in Jinja like:

array['somekey']

but what if i want to access a value deeper in the nested list like:

array['somekey.subkey']

These dots inside the square brackets don't work so i can't access a value at a variable position?

Imagine a simple array of objects like the one below. In the template i want to set a variable that defines the path of the object i want to reach so that if the path is somekey1 i get somevalue1 but if the path is somekey3.subkey2 i get subvalue2 like:

python code

path = 'somekey3.subkey2'

jinja template code

{{ array[path] }}

expected result:

subvalue2

actual result:

no result (nothing gets printed on the screen)

full json array:

[{
    "somekey1": "somevalue1"
}, {
    "somekey2": "somevalue2"
}, {
    "somekey3": {
        "subkey1": "subvalue1",
        "subkey2": "subvalue2"
    }
}]

Thanks in advance!


Solution

  • As my comment says, you'll likely need a custom filter to do dot access – luckily it's not hard to implement:

    import jinja2
    
    
    @jinja2.pass_environment
    def dot(env: jinja2.Environment, obj, path):
        for key in path.split("."):
            obj = env.getattr(obj, key)  # use same semantics as jinja's own getattr
        return obj
    
    
    env = jinja2.Environment()
    
    env.filters["dot"] = dot
    
    tpl = env.from_string("""
    {{ obj | dot('somekey3.subkey2') }}
    """.strip())
    
    print(tpl.render(obj={
        "somekey3": {
            "subkey1": "subvalue1",
            "subkey2": "subvalue2"
        }
    }))
    

    The output is subvalue2 as expected.

    You could expand this to do index accesses too with judicious use of isdigit.