SSF
SSF

Reputation: 983

Create a string using Jinja2 template

I want to convert this variable:

default_attr:
    attr1    :
    - "1"
    nexatt  :
    - "b"
 ...

to "attr=1,nextattr=b,..." (i.e.comma separated string) using Jinja template. Is there a possible way to do this?

- name: Reading the attributes
  set_fact:
    app_attributes: |
        {% set attributes = " " -%}
        {% for key in default_attr.keys() -%}
           {% for value in default_attr[key] -%}
               {% attributes: "{{ attributes }} + [{'key=value'}]" -%}
           {%- endfor %}
        {%- endfor %}
        {{ attributes }}

The error I get is shown below:

fatal: [dev1]: FAILED! => {"msg": "template error while templating string: Encountered unknown tag 'attributes'. Jinja was looking for the following tags: 'endfor' or 'else'. The innermost block that needs to be closed is 'for'.. String: {% set attributes = \" \" -%}\n{% for key in default_attr.keys() -%}\n   {% for value in default_attr[key] -%}\n       {% attributes: \"{{ attributes }} + [{'key=value'}]\" -%}\n   {%- endfor %}\n{%- endfor %}\n{{ attributes }}\n"}

Is there a way to construct this string using Jinja?

Upvotes: 0

Views: 2709

Answers (3)

Nick Guenther
Nick Guenther

Reputation: 161

It doesn't really make sense to try to have those extraneous -s in your input. They mean the values are lists and like @Kanwar said almost any answer we could give will only work if there's only one item in each list. To see why, imagine you had

default_attr:
    attr1    :
    - "1"
    nexatt  :
    - "b"
    - "c"
    - "d"

then what should come out? attr1=1,nextatt=b,nextatt=c,nextatt=d? attr1=1,nextatt=d? attr1=1,nextatt=b? It's not clearly defined.

Judging from your goal, I'm going to assume you meant to ask about this object instead:

default_attr:
    attr1    : "1"
    nexatt  : "b"

and in this case you can do this with one line of filters instead of a template:

"{{ default_attr | items | map('join', '=') | join(',') }}"

items converts a dictionary into a list of [key, value] lists and the nested lists can be flattened by maping join over them, which makes a list of strings, and the final join flattens that list to a single string.

This will produce

"attr1=1,nexatt=b"

Upvotes: 1

Webster
Webster

Reputation: 11

You can construct a list of the key/value pairs and then join them as needed:

- hosts: localhost
  connection: local
  gather_facts: no
  vars:
    the_dict:
      My: Dog
      Has: Fleas
  tasks:
    - name: Convert the dict to a list of key=value elements
      ansible.builtin.set_fact:
        kv_list: "{{ kv_list | default([]) | union([[item.key, item.value] | join('=')]) }}"
      loop: "{{ the_dict | dict2items }}"

- name: Print out a comma-separated string 
  ansible.builtin.debug:
    msg: "{{ kv_list | join(',') }}"

This produces "msg": "My=Dog,Has=Fleas"

Upvotes: 1

Kanwar Saad
Kanwar Saad

Reputation: 2327

Its a bit dirty way but for the sake of answer the snippet below should work for what you have described. One problem is that you have not specified that what will happen when there are more than one items in attr1 or any other attr list. This snippet will work if there is only one item in each list.

- set_fact:
    default_attr:
        attr1    :
        - "1"
        nexatt  :
        - "b"
- set_fact: app_attributes="{{ default_attr | to_json | regex_replace('\:\ ','=') | regex_replace('[\[\]{}\"]') }}"
- debug: var=app_attributes

Upvotes: 2

Related Questions