AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible from_json & to_json Filters: Parse & Convert JSON (Guide)

By Luca Berton · Published 2026-04-03 · Category: troubleshooting

How to use Ansible from_json and to_json filters. Parse JSON strings, convert variables to JSON, process API responses, and write JSON files with examples.

Ansible's from_json and to_json filters let you convert between JSON strings and Ansible data structures (dictionaries and lists).

from_json: Parse JSON String

- name: Parse JSON string
  ansible.builtin.set_fact:
    server_config: "{{ '{\"host\": \"db.example.com\", \"port\": 5432}' | from_json }}"

- name: Access parsed data ansible.builtin.debug: msg: "Database: {{ server_config.host }}:{{ server_config.port }}"

See also: Ansible to_yaml & to_nice_yaml Filters: Format Data as YAML (Guide)

Parse JSON from Command Output

- name: Get Docker container info
  ansible.builtin.command: docker inspect mycontainer
  register: docker_output

- name: Parse container config ansible.builtin.set_fact: container_info: "{{ docker_output.stdout | from_json }}"

- name: Show container IP ansible.builtin.debug: msg: "Container IP: {{ container_info[0].NetworkSettings.IPAddress }}"

Parse JSON from API Response

- name: Call REST API
  ansible.builtin.uri:
    url: https://api.example.com/servers
    method: GET
    headers:
      Authorization: "Bearer {{ api_token }}"
  register: api_response

- name: Process API data (already parsed) ansible.builtin.debug: msg: "Server: {{ item.name }}" loop: "{{ api_response.json.servers }}"

Note: The uri module automatically parses JSON responses into response.json.

See also: Ansible map Filter: Extract Attributes from Lists (Complete Guide)

Read JSON File

- name: Read JSON file
  ansible.builtin.slurp:
    path: /opt/app/config.json
  register: config_raw

- name: Parse config ansible.builtin.set_fact: app_config: "{{ config_raw.content | b64decode | from_json }}"

Or use the file lookup:

- name: Load JSON from file
  ansible.builtin.set_fact:
    app_config: "{{ lookup('file', '/opt/app/config.json') | from_json }}"

to_json: Serialize to JSON

- name: Convert variable to JSON string
  ansible.builtin.debug:
    msg: "{{ my_dict | to_json }}"

- name: Pretty-print JSON ansible.builtin.debug: msg: "{{ my_dict | to_nice_json }}"

See also: Ansible map vs selectattr vs json_query: Filter Data the Right Way

Write JSON to File

- name: Write config as JSON
  ansible.builtin.copy:
    content: "{{ app_config | to_nice_json }}"
    dest: /opt/app/config.json
    mode: '0644'

to_nice_json: Formatted Output

- name: Generate pretty JSON
  ansible.builtin.copy:
    content: |
      {{ config | to_nice_json(indent=2) }}
    dest: /etc/myapp/settings.json

Practical Examples

Transform JSON data

- name: Extract server names from JSON
  ansible.builtin.set_fact:
    server_names: "{{ api_response.json | map(attribute='name') | list | to_json }}"

Merge JSON configs

- name: Load defaults and overrides
  ansible.builtin.set_fact:
    defaults: "{{ lookup('file', 'defaults.json') | from_json }}"
    overrides: "{{ lookup('file', 'overrides.json') | from_json }}"

- name: Merge configurations ansible.builtin.set_fact: final_config: "{{ defaults | combine(overrides, recursive=True) }}"

FAQ

When do I need from_json vs just accessing .json?

The uri module auto-parses JSON responses. Use from_json when you have a raw JSON string from command, shell, slurp, or file lookups.

What's the difference between to_json and to_nice_json?

to_json produces compact single-line JSON. to_nice_json adds indentation and newlines for readability.

How do I handle invalid JSON?

Use default filter: {{ maybe_json | from_json | default({}) }} — but note this may not catch all errors. Validate JSON before parsing.

Parse JSON String

- vars:
    json_string: '{"name": "myapp", "version": "2.0", "port": 8080}'
  set_fact:
    config: "{{ json_string | from_json }}"

- debug: msg: "App {{ config.name }} runs on port {{ config.port }}"

Convert to JSON

- vars:
    my_data:
      servers:
        - host: web1
          port: 80
        - host: web2
          port: 80
  debug:
    msg: "{{ my_data | to_json }}"
    # {"servers": [{"host": "web1", "port": 80}, ...]}

Pretty-Print JSON

- name: Write formatted config file
  ansible.builtin.copy:
    content: "{{ app_config | to_nice_json }}"
    dest: /etc/myapp/config.json
  become: true

Parse API Response

- name: Call API
  ansible.builtin.uri:
    url: https://api.example.com/status
    return_content: true
  register: api_response

- name: Parse response set_fact: status: "{{ api_response.content | from_json }}"

- debug: msg: "Service is {{ status.state }}, uptime: {{ status.uptime_hours }}h"

Read JSON File

- name: Read local JSON
  set_fact:
    config: "{{ lookup('file', 'files/config.json') | from_json }}"

- name: Read remote JSON ansible.builtin.slurp: src: /etc/myapp/config.json register: remote_config

- set_fact: config: "{{ remote_config.content | b64decode | from_json }}"

Modify and Write Back

- name: Read config
  ansible.builtin.slurp:
    src: /etc/myapp/config.json
  register: current

- name: Update config vars: config: "{{ current.content | b64decode | from_json }}" updated: "{{ config | combine({'debug': true, 'log_level': 'verbose'}) }}" ansible.builtin.copy: content: "{{ updated | to_nice_json }}" dest: /etc/myapp/config.json become: true

JSON Filters Reference

| Filter | Description | |--------|-------------| | from_json | Parse JSON string → dict/list | | to_json | Convert to compact JSON string | | to_nice_json | Convert to indented JSON string | | from_yaml | Parse YAML string | | to_yaml | Convert to YAML string | | to_nice_yaml | Convert to readable YAML |

FAQ

What if the JSON is invalid?

The task fails with a parsing error. Validate first:

- name: Check valid JSON
  assert:
    that: my_var | from_json is mapping
    fail_msg: "Invalid JSON"
  ignore_errors: true

from_json vs uri module's auto-parsing?

The uri module auto-parses JSON responses into json attribute. Use from_json for strings from other sources (files, command output, variables).

How do I handle nested JSON?

msg: "{{ (api_response.content | from_json).data.items[0].name }}"

Parse JSON String (from_json)

- vars:
    json_string: '{"name": "myapp", "version": "2.0", "port": 8080}'
  set_fact:
    app_info: "{{ json_string | from_json }}"

- debug: msg: "{{ app_info.name }} v{{ app_info.version }} on port {{ app_info.port }}"

Parse Command Output

- command: curl -s https://api.example.com/status
  register: api_response

- set_fact: status: "{{ api_response.stdout | from_json }}"

- debug: msg="API version {{ status.version }}, uptime {{ status.uptime }}"

Convert to JSON (to_json)

- vars:
    config:
      database:
        host: db.internal
        port: 5432
      features:
        - caching
        - logging
  debug:
    msg: "{{ config | to_json }}"
    # {"database": {"host": "db.internal", "port": 5432}, "features": ["caching", "logging"]}

Pretty Print (to_nice_json)

- debug:
    msg: "{{ config | to_nice_json }}"
    # {
    #     "database": {
    #         "host": "db.internal",
    #         "port": 5432
    #     },
    #     "features": [
    #         "caching",
    #         "logging"
    #     ]
    # }

# Custom indent - debug: msg: "{{ config | to_nice_json(indent=2) }}"

Write JSON to File

- copy:
    content: "{{ app_config | to_nice_json }}"
    dest: /etc/myapp/config.json
  become: true

Read JSON File

# From controller (lookup)
- set_fact:
    config: "{{ lookup('file', 'config.json') | from_json }}"

# From remote host (slurp) - slurp: { src: /etc/myapp/config.json } register: remote_config

- set_fact: config: "{{ remote_config.content | b64decode | from_json }}"

Transform API Responses

- uri:
    url: https://api.example.com/servers
    return_content: true
  register: servers_response

- set_fact: active_servers: "{{ servers_response.json | json_query('results[?status==`active`].name') }}"

JSON in Templates

{# config.json.j2 #}
{{ config_data | to_nice_json }}
- template:
    src: config.json.j2
    dest: /etc/myapp/config.json

Filter Reference

| Filter | Description | |--------|-------------| | from_json | JSON string → Python object | | to_json | Python object → compact JSON | | to_nice_json | Python object → formatted JSON | | to_nice_json(indent=2) | Custom indentation | | from_yaml | YAML string → Python object | | to_yaml | Python object → compact YAML | | to_nice_yaml | Python object → formatted YAML | | json_query() | JMESPath query on JSON |

FAQ

from_json vs accessing .json directly?

uri module returns .json already parsed. from_json is for raw strings from command, slurp, or lookup.

How do I handle invalid JSON?

- set_fact:
    data: "{{ raw_string | from_json }}"
  rescue:
    - debug: msg="Invalid JSON received"

to_json vs to_nice_json?

to_json produces compact single-line output (good for APIs). to_nice_json produces human-readable formatted output (good for config files).

Related Articles

dynamic config with Ansible templateAnsible JSON Conversion Guidethe Ansible Docker referencecapturing output with Ansible commandloop_control in Ansible

Category: troubleshooting

Browse all Ansible tutorials · AnsiblePilot Home