Free tech learning for everyone, from basics to pro

APIsProgramming and Application SecurityBeginner

JSON and XML for APIs: structure, parsing and pitfalls

In short: JSON uses objects and arrays and maps directly onto Python dictionaries and lists. XML uses nested elements and attributes and is still used by NETCONF, SOAP and many enterprise tools. Parse both with standard libraries, validate what you receive, and avoid unsafe XML parsers on untrusted input.

Why data formats matter

An API sends text over the network. Both sides must agree on how that text is structured, so the receiver can turn it back into data. The two formats you will meet most are JSON (JavaScript Object Notation) and XML (Extensible Markup Language). YAML, which you may know from Ansible, is a close relative of JSON designed for humans to write.

JSON vs XMLJSONObjects and arraysCompact, easy to readNative to most REST APIsFew data typesXMLElements and attributesVerbose but strictNETCONF and SOAPSchemas and namespaces
Figure 1. JSON dominates REST APIs; XML remains important for NETCONF, SOAP and some enterprise systems.

JSON in detail

JSON has only a handful of building blocks: objects (key-value pairs in braces), arrays (ordered lists in square brackets), strings in double quotes, numbers, true, false and null. Keys must be strings in double quotes, and trailing commas are not allowed, which is the most common cause of invalid JSON.

{
  "hostname": "edge-fw1",
  "site": "london",
  "managed": true,
  "interfaces": [
    {"name": "ethernet1/1", "zone": "untrust", "ip": "203.0.113.10/29"},
    {"name": "ethernet1/2", "zone": "trust", "ip": "192.168.10.1/24"}
  ]
}
JSONPython
objectdict
arraylist
stringstr
numberint or float
true / falseTrue / False
nullNone
From API text to Python dataResponse textJSON stringParsejson.loads()Python datadicts andlistsUse itloop, filter,reportSerialisejson.dumps()
Figure 2. Parsing turns text into Python objects; serialising turns them back into text to send.
import json

text = '{"hostname": "edge-fw1", "interfaces": [{"name": "ethernet1/1"}]}'
data = json.loads(text)                 # str  -> dict
for intf in data["interfaces"]:
    print(data["hostname"], intf["name"])

payload = {"vlan_id": 30, "name": "VOICE"}
body = json.dumps(payload, indent=2)    # dict -> str
with open("vlan.json", "w", encoding="utf-8") as f:
    json.dump(payload, f, indent=2)

Remember: loads and dumps work with strings (the s stands for string); load and dump work with files.

XML in detail

XML wraps data in opening and closing tags. Elements can nest, carry attributes, and belong to namespaces that stop names from different vendors clashing. NETCONF, for example, sends configuration as XML described by YANG models.

<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
  <interface>
    <name>GigabitEthernet1</name>
    <enabled>true</enabled>
  </interface>
</interfaces>
from defusedxml import ElementTree as ET   # pip install defusedxml

NS = {"if": "urn:ietf:params:xml:ns:yang:ietf-interfaces"}
root = ET.fromstring(xml_text)
for intf in root.findall("if:interface", NS):
    print(intf.find("if:name", NS).text, intf.find("if:enabled", NS).text)

Tip: namespaces are the number one reason findall() returns nothing. If the document declares an xmlns, you must use it in your search.

Security and common mistakes

  • XXE (XML External Entity) attacks abuse XML features that load external files or URLs. Use a hardened parser such as defusedxml for any XML you did not create, and keep server-side parsers configured to refuse external entities.
  • Trusting the shape: a field you expect may be missing or the wrong type. Use dict.get() with defaults, or validate against a JSON Schema before acting on data.
  • Numbers as strings: some APIs return "20" instead of 20. Convert explicitly before comparing.
  • Building JSON by string concatenation: always build a dict and call json.dumps(), so quotes and special characters are escaped correctly.

In exams you may be asked to spot an invalid JSON document or convert a small XML snippet to JSON. At work, pretty-print responses with python3 -m json.tool or jq to see their structure quickly.

Practice

What Python type does a JSON array become after json.loads()?
A list.
Why might root.findall("interface") return an empty list on a NETCONF reply?
The elements are in an XML namespace, so you must search with the namespace prefix and mapping, for example findall("if:interface", NS).
Why use defusedxml rather than the standard parser for XML from outside?
It blocks dangerous features such as external entities and entity expansion that can lead to file disclosure or denial of service.

Key takeaways

  • JSON objects map to Python dicts and arrays map to lists.
  • Use json.loads/dumps for strings and json.load/dump for files.
  • XML uses elements, attributes and namespaces; NETCONF relies on it.
  • Parse untrusted XML with a hardened parser to prevent XXE.
  • Validate fields and types before acting on API data.
Tracks your learning on this device only. See your progress on the learning paths.

About the author

Mohamed Fayas

Founder of FM TecHub. CCNA certified, with a BTEC HND in Network Engineering, and BEng (Hons) in Computer Networking and Cloud Security. He writes practical, plain-English guides on networking, security and cloud.

More about the author

Original content written by Mohamed Fayas for FM TecHub. © 2026 FM TecHub. You're welcome to share a link to this page; please don't copy the text elsewhere without permission.

Stay up to date by email

New notes, tools and security news from FM TecHub, explained simply. Free.

We only use your email to send these updates. See our privacy policy.