Python can deserialize JSON, YAML, and XML files into in-memory data, modify that data, and serialize it again. JSON and XML support is included in Python’s standard library. The examples here use the third-party PyYAML distribution for YAML.
Read Serialization first for the language-neutral process, or API Data Formats for the syntax and trade-offs of each representation.
Before the Code
Three actions that look similar serve different purposes:
python -m pip install PyYAML install files into this Python environment
import yaml load the installed module into this program
yaml.safe_load(file) deserialize YAML dataThe detailed explanations are in:
Set Up the Example
Create and activate a virtual environment before installing the YAML dependency:
python3 -m venv .venv
source .venv/bin/activate
python -m pip install PyYAMLThe json and xml.etree.ElementTree modules need no separate installation because they are part of the standard library.
The examples use the same logical data:
user
├── name: john
├── location
│ ├── city: Austin
│ └── state: TX
└── roles
├── admin
└── userJSON
Python’s json module converts common JSON values as follows:
| JSON | Python |
|---|---|
| object | dict |
| array | list |
| string | str |
| integer number | int |
| real number | float |
true / false | True / False |
null | None |
Given user.json:
{
"user": {
"name": "john",
"location": {
"city": "Austin",
"state": "TX"
},
"roles": ["admin", "user"]
}
}Read it, change the city, and write a new file:
import json
from pathlib import Path
input_path = Path("user.json")
output_path = Path("user-updated.json")
with input_path.open(encoding="utf-8") as file:
data = json.load(file)
for role in data["user"]["roles"]:
print(role)
data["user"]["location"]["city"] = "Dallas"
with output_path.open("w", encoding="utf-8") as file:
json.dump(data, file, indent=2, ensure_ascii=False)
file.write("\n")data is a dictionary. Changing it does not change user.json; the file changes only when the program writes to it.
load vs. loads, and dump vs. dumps
The final s means string:
| Function | Direction |
|---|---|
json.load(file) | File-like object to Python data |
json.loads(text) | JSON string or supported byte input to Python data |
json.dump(data, file) | Python data to a file-like object |
json.dumps(data) | Python data to a JSON string |
json_text = '{"enabled": true}'
settings = json.loads(json_text)
new_text = json.dumps(settings)JSON round trips are not guaranteed to preserve the exact Python type. For example, a tuple is written as a JSON array and read back as a list.
YAML with PyYAML
PyYAML is the distribution name; yaml is the import name:
python -m pip install PyYAMLGiven user.yaml:
user:
name: john
location:
city: Austin
state: TX
roles:
- admin
- userRead, modify, and write the data:
from pathlib import Path
import yaml
input_path = Path("user.yaml")
output_path = Path("user-updated.yaml")
with input_path.open(encoding="utf-8") as file:
data = yaml.safe_load(file)
for role in data["user"]["roles"]:
print(role)
data["user"]["location"]["city"] = "Dallas"
with output_path.open("w", encoding="utf-8") as file:
yaml.safe_dump(
data,
file,
sort_keys=False,
allow_unicode=True,
)Use safe_load for YAML received from a file, API, or user. PyYAML’s more permissive constructors can create arbitrary Python objects and are unsafe for untrusted input.
safe_dump emits standard YAML tags. It may change formatting and does not preserve comments from the original document. When round-trip preservation of comments, quoting, and layout matters, evaluate a round-trip-oriented library such as ruamel.yaml.
Note: YAML supports features and types beyond JSON. Do not assume that every YAML document will produce only dictionaries, lists, strings, numbers, booleans, and
None.
XML with ElementTree
XML represents a tree of elements rather than native objects and arrays. xml.etree.ElementTree preserves that tree-shaped model.
Given user.xml:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<user>
<name>john</name>
<location>
<city>Austin</city>
<state>TX</state>
</location>
<roles>
<role>admin</role>
<role>user</role>
</roles>
</user>
</root>Read, traverse, modify, and write the tree:
import xml.etree.ElementTree as ET
tree = ET.parse("user.xml")
root = tree.getroot()
user = root.find("user")
if user is None:
raise ValueError("Missing <user> element")
for role in user.findall("./roles/role"):
print(role.text)
city = user.find("./location/city")
if city is None:
raise ValueError("Missing <city> element")
city.text = "Dallas"
tree.write(
"user-updated.xml",
encoding="utf-8",
xml_declaration=True,
)The main operations are:
| Operation | ElementTree API |
|---|---|
| Parse a file | ET.parse(...) |
| Get the document root | tree.getroot() |
| Find the first matching element | element.find(...) |
| Find all matching elements | element.findall(...) |
| Read or replace element character data | element.text |
| Serialize the tree to a file | tree.write(...) |
Real API XML often uses namespaces. In that case, element searches must include the namespace URI or a prefix map; the visible prefix alone is not the element’s full name.
Why XML Libraries Feel Different
XML libraries expose different models:
| Library | Model exposed to Python | Installation |
|---|---|---|
xml.etree.ElementTree | Element tree | Standard library |
xml.dom.minidom | Document Object Model nodes | Standard library |
xmltodict | Dictionary-like mapping chosen by the library | Third-party |
untangle | Attribute-style object interface | Third-party |
Converting XML to a dictionary can be convenient, but XML has attributes, namespaces, repeated elements, mixed content, and ordering. A dictionary conversion needs library-specific rules for those features. ElementTree keeps the XML tree explicit.
Deserialization Is Only the First Check
Successful parsing means the representation is syntactically acceptable. It does not mean the content is safe or correct.
roles = data.get("user", {}).get("roles")
if not isinstance(roles, list):
raise ValueError("user.roles must be a list")For production code:
- limit input sizes where possible
- handle parse errors
- validate required fields and data types
- write to a new file when preserving the original matters
- use atomic replacement when partially written output would be dangerous
- follow the API’s schema rather than guessing field meanings
The Python json documentation warns that maliciously large or deeply nested input can consume considerable CPU and memory. XML and YAML parsers also require defensive limits when processing untrusted data.
What About pickle?
pickle is Python-specific serialization that can preserve more Python object structure than JSON, YAML, or XML. It is not a general API interchange format.
Never unpickle untrusted data. A crafted pickle can execute arbitrary code during deserialization.
TL;DR
- JSON and XML support comes with Python; PyYAML is installed separately.
pip install,import, and deserialization are three different operations.- Use
json.loadandjson.dumpfor files; useloadsanddumpsfor strings. - Use
yaml.safe_loadandyaml.safe_dumpfor ordinary YAML data. - ElementTree keeps XML as a tree rather than forcing it into a dictionary.
- In-memory changes do not affect the source file until the program writes output.
- Parsing does not replace schema and business-rule validation.
- Never use
picklewith untrusted data.
Resources
- Python
jsonmodule - PyYAML documentation
- Python ElementTree
- Python XML security notes
- Python
picklemodule and security warning
Note: Examples and security guidance were checked against current Python and PyYAML documentation in July 2026.