Useful Jinja Filters and Extension in APIO
Key Filters
- tojson: Converts a dictionary to a JSON Object.
{{ data | tojson }}
- json: Converts a JSON string into a dictionary for further manipulation.
{{ context.response | json }}
- pp_dict: Formats a dictionary into a
pretty-printed
JSON Object.
{{ data | pp_dict }}
- combine: Merges dictionaries.
{{ request.body | combine(context.response | json) }}
- dict_filter: Filters dictionary entries by key.
{{ request.body | dict_filter("firstName") }}
Constructing Dynamic JSON Structure
Example: Including only non-empty values
Let's start with a simple example, we have the dictionary context provided in the APIO environment.
{
"context":{
"eid": {"value": "12345"},
"iccid": "",
"profileType": "test"
}
}
Let's say that we want to construct a JSON Object inside an APIO workflow including only the keys in the dictionary context containing non-empty values. We can use the following solution where we benefit from Jinja's ability to conditionally include attributes and handle commas dynamically.
{% set data = {} -%}
{% if context.eid -%}
{% set _ = data.update({"eid": context.eid}) -%}
{% endif -%}
{% if context.iccid -%}
{% set _ = data.update({"iccid": context.iccid}) -%}
{% endif -%}
{% if context.profileType -%}
{% set _ = data.update({"profileType": context.profileType}) -%}
{% endif -%}
{{ data | tojson }}
Where data is an empty dictionary initialized to store key-value pairs dynamically based on some conditions. The if statements check whether specific keys in context contain non-empty values. If a value exists, it is added to the data dictionary using update(). The expression if context.eid
checks whether context.eid holds a truthy value. If this is the case, the dictionary is updated with a key "eid" and its corresponding value from context.eid. The expression {{data | tojson}}
converts the data dictionary into a JSON Object for output.
Exercise
Copy/paste the data from this subsection to the template playground, check the result. Change the context data, and check the result.
Best Practices
Use the Right Notation:
- Use dot notation for valid Python identifiers.
- Use bracket notation for keys with special characters.
Error Handling:
- Ensure JSON strings are valid before parsing.
- Use the | json filter cautiously and validate inputs.
Compact JSON:
- Use | tojson to generate compact JSON Objects for output.
A note on handling throwable variables
In the code, _
is a common convention used in Jinja (and Python) to denote a throwaway variable. It's used when the result of an operation is not needed directly but the operation itself needs to be performed. In the following example,
{% set data = {} -%}
{% if context.eid -%}
{% set _ = data.update({"eid": context.eid}) -%}
{% endif -%}
{% if context.iccid -%}
{% set _ = data.update({"iccid": context.iccid}) -%}
{% endif -%}
{% if context.profileType -%}
{% set _ = data.update({"profileType": context.profileType}) -%}
{% endif -%}
{{ data | tojson }}
the update()
method updates the dictionary data in place with the specified key-value pair (e.g., {"eid": context.eid}
). However, update()
returns None
(it doesn't return the updated dictionary). Assigning the result of update()
(which is None
) to _
serves to perform the update operation without needing the return value. The _
variable is used as a "placeholder" to indicate that the result is intentionally ignored. By convention, we use _
to signal to others reading the code that the variable is not used elsewhere. It is nevertheless required by Jinja: Jinja's {% set %}
requires a variable to assign the result of an operation. Since the operation itself (update) is important but the result is not, _
is used.