Use the combine filter.

- set_fact:
    mergedobject: "{{ object1.params | combine (object2.params) }}"
Answer from Thomas Hirsch on Stack Overflow
🌐
GitHub
gist.github.com › jpralves › 718c233a7e8f02f57fc46fe1a17196ff
Merge the content of a json file with another json file · GitHub
--- - name: get content of original file slurp: src: "original.json" register: original_config ignore_errors: yes - name: convert content to json and load merge json set_fact: original_json: "{{ original_config.content|default('e30=') | b64decode | from_json }}" merge_json: "{{ lookup('file', merge.json' ) | from_json }}" - name: merge content copy: content: "{{ original_json | combine(merge_json) | to_nice_json(indent=2) }}" dest: "new.json"
Discussions

jmespath - Combine attribute value using json_query in ansible - Stack Overflow
I want to combine two attribute into single string separated by delimiter using the json_query in ansible Sample data More on stackoverflow.com
🌐 stackoverflow.com
How do I create and store a list of dict/json objects?
Automation for the People! A Subreddit dedicated to fostering communication in the Ansible Community, includes Ansible, AWX, Ansible Galaxy, ansible-lint, Molecule, etc · So I have this task which properly puts out a dict of repository info collected in a previous task More on reddit.com
🌐 r/ansible
3
1
September 13, 2019
Ansible combine 'setup' output with task output to JSON - Stack Overflow
I currently gather facts about all my hosts using this command: ansible all -m setup --tree out This creates a file for each host in the directory out with all the ansible variables in JSON format. More on stackoverflow.com
🌐 stackoverflow.com
See how merge json with another in ansible
should define a default object user and groups variables to clean tasks More on github.com
🌐 github.com
2
November 11, 2015
🌐
LinuxTut
linuxtut.com › en › e948158fc54a9c6cf232
Merge JSON format data with Ansible
December 21, 2019 - JSONMerge has been imported as an Ansible filter plugin. This is a Python-based module that merges head (additional JSON data) with base (original JSON data) and outputs the result. You can specify the merge method in Merge Strategies. If not specified, it will be ʻoverwriteand the old value ...
🌐
Opensource.com
opensource.com › article › 21 › 4 › process-json-data-ansible
5 ways to process JSON data in Ansible | Opensource.com
April 28, 2021 - If the data is structured, you can leverage a wide array of options to make parsing it simpler and more consistent. Structured data conforms to a data model, which allows access to each data field separately. The data for these models is exchanged as key/value pairs and encoded using different formats. JSON, which is widely used in Ansible, is one of them.
🌐
Google Groups
groups.google.com › g › ansible-project › c › Rra70UVLIac
merge json_query output
March 23, 2021 - https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#selecting-from-sets-or-lists-set-theory
🌐
Ansible
docs.ansible.com › projects › ansible › latest › collections › ansible › builtin › combine_filter.html
ansible.builtin.combine filter – combine two dictionaries — Ansible Community Documentation
This describes positional parameters of the filter. These are the values positional1, positional2 and so on in the following example: input | ansible.builtin.combine(positional1, positional2, ...)
Find elsewhere
🌐
Ansible
docs.ansible.com › ansible › latest › playbook_guide › playbooks_filters.html
Using filters to manipulate data — Ansible Community Documentation
You must use a hardcoded number to define the width, instead of a construction like float("inf"), because the filter does not support proxying Python functions. For example: {{ some_variable | to_yaml(indent=8, width=1337) }} {{ some_variable | to_nice_yaml(indent=8, width=1337) }} The filter does support passing through other YAML parameters. For a full list, see the PyYAML documentation for dump(). If you are reading in some already formatted data: {{ some_variable | from_json }} {{ some_variable | from_yaml }}
Top answer
1 of 2
5

For example

    - debug:
        msg: "{{ locations|
                 json_query('[?state == `WA`].[name,state]')|
                 map('join', '-')|list }}"

gives

  msg:
  - Seattle-WA
  - Bellevue-WA
  - Olympia-WA

The same result gives the task below using Jinja2 filters only

    - debug:
        msg: "{{ _names|zip(_states)|map('join', '-')|list }}"
      vars:
        _locations: "{{ locations|selectattr('state', 'eq', 'WA')|list }}"
        _names: "{{ _locations|map(attribute='name')|list }}"
        _states: "{{ _locations|map(attribute='state')|list }}"

json_query issue (fixed in 2.10 and later)

There is JMESPath join. Unfortunately

    - debug:
        msg: "{{ locations|
                 json_query('[].join(`-`, [name,state])') }}"

fails

msg: |- JMESPathError in json_query filter plugin: In function join(), invalid type for value: Seattle, expected one of: ['array-string'], received: "AnsibleUnicode"

to_json|from_json workaround

Quoting from json_query: Add examples for starts_with and contains #72821

data structure returned from register variables needs to be parsed using to_json | from_json in order to get a correct result. Fixes: ansible-collections/community.general#320

    - debug:
        msg: "{{ locations|to_json|from_json|
                 json_query('[].join(`-`, [name,state])') }}"

gives

  msg:
  - Seattle-WA
  - New York-NY
  - Bellevue-WA
  - Olympia-WA
2 of 2
1

Just for the sake of a pure JMESPath way of doing it, as your trial and error solution still have an unneeded extra layer of complexity.

When you are doing

[?state == 'WA'].[join('-', [name, state])][]

You are creating an array [join('-', [name, state])] then flattening it [] for no reason.

You can just go to the solution with a shorter approach:

[?state == `WA`].join(`-`, [name, state])

Also mind that you can overcome the quotes in quotes (simple or double) complication for JMESPath queries using:

  1. YAML multilines string: How do I break a string in YAML over multiple lines?

  2. Backticks in your JMESPath query, as pointed in the documentation:

    In the example above, quoting literals using backticks avoids escaping quotes and maintains readability.

    Source: https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#selecting-json-data-json-queries


So you end up with (see note below if you are on an Ansible version < 2.10):

- debug:
    msg: >-
      {{ test.locations 
           | json_query('[?state == `WA`].join(`-`, [name, state])') }}

Please note: as raised by @Vladimir Botka on the versions prior to 2.10, you will be affected by this issue: https://github.com/ansible/ansible/issues/27299#issuecomment-331068246, forcing you to add a | to_json | from_json filter on the list.


Given the playbook:

- hosts: all
  gather_facts: yes

  tasks:
    - debug:
        msg: >-
          {{ test.locations 
              | json_query('[?state == `WA`].join(`-`, [name, state])') 
          }}
      vars:
        test:
          locations:
            - name: Seattle
              state: WA
            - name: New York
              state: NY
            - name: Bellevue
              state: WA
            - name: Olympia
              state: WA

This yields:

[
    "Seattle-WA",
    "Bellevue-WA",
    "Olympia-WA"
]
🌐
Reddit
reddit.com › r/ansible › how do i create and store a list of dict/json objects?
r/ansible on Reddit: How do I create and store a list of dict/json objects?
September 13, 2019 - - name: "Build list of dicts" set_fact: new_dict: " {{ new_list | combine(item.json ) }} " with_items: "{{ (artifactory_repo_info.results | list )}}" I get an error when I attempt this. Im sure there is a simple solution or this is one of those shortcomings of Ansible ·
Top answer
1 of 1
5

There are more options:

  1. Convert the dictionary management
    - set_fact:
        mgmt: "{{ mgmt|d({})|
                  combine({item.key: {'Property4': item.value}}) }}"
      loop: "{{ management|dict2items }}"

If you want to avoid iteration use:

a) the filter community.general.dict_kv

    mgmt: "{{ dict(management.keys()|
                   zip(management.values()|
                       map('community.general.dict_kv', 'Property4'))) }}"

b) the filter community.general.json_query

    mgmt: "{{ dict(management|dict2items|
                   json_query('[].[key, {Property4: value}]')) }}"

c) Jinja

    mgmt_str: |
      {% for k,v in management.items() %}
      {{ k }}:
        Property4: {{ v }}
      {% endfor %}
    mgmt: "{{ mgmt_str|from_yaml }}"

All options give the same result

  mgmt:
    server1:
      Property4: ip1_addr
    server2:
      Property4: ip2_addr

Then, combine the dictionaries

  srvs: "{{ servers|combine(mgmt, recursive=True) }}"

gives the desired result

  srvs:
    server1:
      Property1: A
      Property2: B
      Property3: C
      Property4: ip1_addr
    server2:
      Property1: A
      Property2: B
      Property3: C
      Property4: ip2_addr

Example of a complete playbook for testing

- hosts: localhost

  vars:

    servers:
      server1:
        Property1: A
        Property2: B
        Property3: C
      server2:
        Property1: A
        Property2: B
        Property3: C
        
    management:
      server1: ip1_addr
      server2: ip2_addr

    mgmt: "{{ dict(management.keys()|
                   zip(management.values()|
                       map('community.general.dict_kv', 'Property4'))) }}"
    srvs: "{{ servers|combine(mgmt, recursive=True) }}"

  tasks:

    - debug:
        var: mgmt
    - debug:
        var: srvs

  1. Use Jinja to create the YAML structure and convert it using the filter from_yaml. The declaration of srvs below gives the same result
    srvs_str: |
      {% for k,v in servers.items() %}
      {{ k }}:
         {{ v|combine({'Property4': management[k]}) }}
      {% endfor %}
    srvs: "{{ srvs_str|from_yaml }}"

  1. If you want to iterate the result it's easier to convert both dictionaries to lists
    - set_fact:
        mgmt_list: "{{ mgmt_list|d([]) +
                       [{'server': item.key, 'Property4': item.value}] }}"
      loop: "{{ management|dict2items }}"

    - set_fact:
        srvs_list: "{{ srvs_list|d([]) +
                       [{'server': item.key}|combine(item.value)] }}"
      loop: "{{ servers|dict2items }}"

If you want to avoid iteration use json_query

    mgmt_list: "{{ management|dict2items(key_name='server',
                                         value_name='Property4') }}"
    srvs_list: "{{ servers|dict2items|
                   json_query('[].merge({server: key}, value)') }}"

give

  mgmt_list:
  - Property4: ip1_addr
    server: server1
  - Property4: ip2_addr
    server: server2

  srvs_list:
  - Property1: A
    Property2: B
    Property3: C
    server: server1
  - Property1: A
    Property2: B
    Property3: C
    server: server2

Then use Community.General filter community.general.lists_mergeby

  srvs: "{{ srvs_list|community.general.lists_mergeby(mgmt_list, 'server') }}"

gives a list, instead of the expected dictionary

  srvs:
  - Property1: A
    Property2: B
    Property3: C
    Property4: ip1_addr
    server: server1
  - Property1: A
    Property2: B
    Property3: C
    Property4: ip2_addr
    server: server2

You can convert the list to the dictionary

  srvs_dict: "{{ dict(srvs|map(attribute='server')|zip(srvs)) }}"

, or

  srvs_dict: "{{ dict(srvs|json_query('[].[server, @]')) }}"

Both options give the same result

  srvs_dict:
    server1:
      Property1: A
      Property2: B
      Property3: C
      Property4: ip1_addr
      server: server1
    server2:
      Property1: A
      Property2: B
      Property3: C
      Property4: ip2_addr
      server: server2

Example of a complete playbook for testing

- hosts: localhost

  vars:

    servers:
      server1:
        Property1: A
        Property2: B
        Property3: C
      server2:
        Property1: A
        Property2: B
        Property3: C
        
    management:
      server1: ip1_addr
      server2: ip2_addr

    mgmt_list: "{{ management|dict2items(key_name='server',
                                         value_name='Property4') }}"
    srvs_list: "{{ servers|dict2items|
                   json_query('[].merge({server: key}, value)') }}"
    srvs: "{{ srvs_list|community.general.lists_mergeby(mgmt_list, 'server') }}"
    
  tasks:

    - debug:
        var: mgmt_list
    - debug:
        var: srvs_list
    - debug:
        var: srvs

Top answer
1 of 2
3

As @DustWolf notes in the comments,

For anyone from the Internet looking for the answer to: "How tp combine nested dictionaries in ansible", the answer is | combine(new_item, recursive=true)

This solves a closely related issue that has baffled myself and my team for months.

I will demonstrate:

Code:

---
- hosts: localhost
  gather_facts: false
  vars:
    my_default_values:
      key1: value1
      key2:
        a: 10
        b: 20
    my_custom_values:
      key3: value3
      key2:
        a: 30
    my_values: "{{ my_default_values | combine(my_custom_values, recursive=true) }}"
  tasks:
    - debug: var=my_default_values
    - debug: var=my_values

Output:

ok: [localhost] => 
  my_values:
    key1: value1
    key2:
      a: 30
    key3: value3

Note how key2 was completely replaced, thus losing key2.b

We changed this to:

my_values: "{{ my_default_values | combine(my_custom_values, recursive=true) }}"

Output:

  my_values:
    key1: value1
    key2:
      a: 30
      b: 20
    key3: value3
2 of 2
0

This syntax is not legal, or at the very least doesn't do what you think:

new_item: "{ '{{item.key}}' : { 'landscape': '{{landscape_dictionary[item.key]|default(false)}}' } }"

Foremost, ansible will only auto-coerce JSON strings into a dict, but you have used python syntax.

Secondarily, the way to dynamically construct a dict is not to use jinja2 to build up text but rather use the fact that jinja2 is almost a programming language:

new_item: "{{
  {
    item.key: {
      'landscape': landscape_dictionary[item.key]|default(false)
    }
  }
}}"

Any time you find yourself with nested jinja2 interpolation blocks, that is a code smell that you are thinking about the problem too much as text (by nested, I mean {{ something {{nested}} else }})

🌐
Ansible
forum.ansible.com › archives › ansible project
how to join two json data structure to perform a join operation - Ansible Project - Ansible
September 8, 2017 - Hi I am very new with ansible and I am struggling to solve the following “problem” I have two json inputs a list of “locations” (you can think them as stores). For each "location"I have the name and the unique ID and a list of “parts” . For each item, I have an array which tells ...
🌐
Htunnthuthu
blog.htunnthuthu.com › automation › automation › ansible-101 › ansible-101-json-usage-in-ansible
JSON Usage in Ansible | Tech With Htunn
This filter implements JMESPath, a query language for JSON. It's invaluable for extracting specific data from complex structures. Before using json_query, you need to install the JMESPath Python module on your Ansible ...
🌐
GitHub
github.com › IBM › ansible-role-infosvr-import-export › issues › 23
Add module to merge multiple relationship JSON files · Issue #23 · IBM/ansible-role-infosvr-import-export
October 1, 2018 - Add a new module that will merge multiple relationship JSON files into a single file. This could then be used to merge a relationship file from a source environment with an export from the target e...
Author   cmgrote