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

FilterDescription
from_jsonParse JSON string → dict/list
to_jsonConvert to compact JSON string
to_nice_jsonConvert to indented JSON string
from_yamlParse YAML string
to_yamlConvert to YAML string
to_nice_yamlConvert 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

FilterDescription
from_jsonJSON string → Python object
to_jsonPython object → compact JSON
to_nice_jsonPython object → formatted JSON
to_nice_json(indent=2)Custom indentation
from_yamlYAML string → Python object
to_yamlPython object → compact YAML
to_nice_yamlPython 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).

Category: troubleshooting

Browse all Ansible tutorials · AnsiblePilot Home