I have following json file
[
{
"v4-filter": {
"accept_to_test1t": {
"action": "accept"
},
"access_to_test2": [
{
"source-prefix-list": "x.x.x.x/yy"
},
{
"destination-prefix-list": "x.x.x.x/yy"
},
{
"action": "accept"
}
]
}
}
]
Please note that for dictionary key accept_to_test1 value is again a dictionary "action": "accept" Problem: I need to convert all the nested dictionaries like case above in a list. In this specific case, for example
accept_to_test1 : [{"action": "accept"}]. All the rest stay as it is. Result:
[
{
"v4-filter": {
"accept_to_test1t": [
{
"action": "accept"
}],
"access_to_test2": [
... like before ...
}
}
]
I am using a jinja2 template for rendering this data and it works but only for list of dictionary.
{%- for d in data_list -%}
{%- for k,v in d.items() -%}
{%- for term,value in v.items() -%}
term {{ term }} {{ '{ \n' }}
{%- set label = namespace(source=false, dest=false, port=false, action=false, prot=false) -%}
{%- for dict_item in value -%}
{%- for key, value in dict_item.items() -%}
{% if key == "source-prefix-list" %}
{% if not label.source %} source-address::{% else %}{{ ' '*18 }}{% endif %} {{value}} {{ '\n' }}
{%- set label.source = true -%}
{% endif %}
{%- if key == "destination-prefix-list" -%}
{% if not label.dest %} destination-address::{% else %}{{ ' '*20 }}{% endif %} {{value}} {{ '\n' }}
{%- set label.dest = true %}
{%- endif -%}
{%- if key == "destination-port" -%}
{% if not label.port %} destination-port::{% else %}{{ ' '*20 }}{% endif %} {{value}} {{ '\n' }}
{%- set label.port = true -%}
{% endif %}
{%- if key == "action" -%}
{% if not label.action %} action::{% else %}{{ ' '*20 }}{% endif %} {{value}} {{ '\n' }}
{%- set label.action = true -%}
{% endif %}
{%- if key == "protocol" -%}
{% if not label.protocol %} protocol::{% else %}{{ ' '*20 }}{% endif %} {{value}} {{ '\n' }}
{%- set label.protocol = true -%}
{% endif %}
{%- endfor -%}
{%- endfor -%}
{{ '} \n\n' }}
{%- endfor -%}
{%- endfor -%}
{%- endfor -%}
{# above template is for json file #}
Problem is here during rendering
{%- for key, value in dict_item.items() -%}
jinja2.exceptions.UndefinedError: 'str object' has no attribute 'item
Due to {'action': 'accept'} that is not an iterable item. It would work with something like [{'action': 'accept'}].
Any hint is welcome.
Thanks.
CodePudding user response:
A simple solution for this issue is just to detect when the value variable is a dict, then embed it in a list. For the detection we can use the mapping bultin test. The embedding is a simple encapsulation: {%- set value_list = [value] %}.
{%- for d in data_list -%}
{%- for k,v in d.items() -%}
{%- for term,value in v.items() -%}
term {{ term }} {{ '{ \n' }}
{%- set label = namespace(source=false, dest=false, port=false, action=false, prot=false) -%}
{%- if value is mapping() %}
{%- set value_list = [value] %}
{%- else %}
{%- set value_list = value %}
{%- endif %}
{%- for dict_item in value_list -%}
{%- for key, value in dict_item.items() -%}
{%- if key == "source-prefix-list" %}
{%- if not label.source %} source-address::{% else %}{{ ' '*18 }}{% endif %} {{value}} {{ '\n' }}
{%- set label.source = true -%}
{%- endif %}
{%- if key == "destination-prefix-list" -%}
{%- if not label.dest %} destination-address::{% else %}{{ ' '*20 }}{% endif %} {{value}} {{ '\n' }}
{%- set label.dest = true %}
{%- endif -%}
{%- if key == "destination-port" -%}
{%- if not label.port %} destination-port::{% else %}{{ ' '*20 }}{% endif %} {{value}} {{ '\n' }}
{%- set label.port = true -%}
{%- endif %}
{%- if key == "action" -%}
{%- if not label.action %} action::{% else %}{{ ' '*20 }}{% endif %} {{value}} {{ '\n' }}
{%- set label.action = true -%}
{%- endif %}
{%- if key == "protocol" -%}
{%- if not label.protocol %} protocol::{% else %}{{ ' '*20 }}{% endif %} {{value}} {{ '\n' }}
{%- set label.protocol = true -%}
{%- endif %}
{%- endfor -%}
{%- endfor -%}
{{ '} \n\n' }}
{%- endfor -%}
{%- endfor -%}
{%- endfor -%}
Output:
term accept_to_test1t {
action:: accept
}
term access_to_test2 {
source-address:: x.x.x.x/yy
destination-address:: x.x.x.x/yy
action:: accept
}
I've also fixed some indentation problems.
