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 template • Ansible JSON Conversion Guide • the Ansible Docker reference • capturing output with Ansible command • loop_control in AnsibleCategory: troubleshooting