XML to JSON Converter
Convert XML to JSON — and back — with explicit attribute, array, and mixed-content semantics, entirely on this device.
Press ? for shortcuts
Do it offline:$ yq -p xml -o json feed.xml
How to use it
- Paste XML or JSON, or drop a file — the badge names the detected direction; a click or the s key overrides it.
- Set the direction's options — attribute mode, always-array names, coercion, namespaces, comments, empty elements — with inline help.
- Read the shape report under the output: it counts every attribute, array and mixed-content decision.
- Copy or download the result (.json or .xml), or share the state as a link — credential-shaped input is never stored.
XML carries attributes, repeated elements and mixed content — JSON has no native slot for any of them, so every converter picks a mapping. This tool makes the mapping explicit — @-prefixed attributes, an always-array list, a #text key — and counts each decision in a shape report. Best for data-shaped XML; markup woven through prose loses structure, and XSD validation and XSLT are out of scope.
How it works
An XML element can state a fact twice — id="7" as an attribute, <id> as a child — so attributes take an @ prefix: reversible, collision-proof. Merge writes plain keys, falling back to @ on a clash; drop discards and counts.
Repetition drives arrays: three <item> siblings convert to an array, one to a lone value — a feed's JSON shape follows its entry count. The always-array list pins names to arrays at every count.
Text beside element children is mixed content: it survives on #text with a warning, because JSON cannot record how text and elements interleaved.
Coercion defaults off: XML text is untyped and guessing is lossy — 007 becomes 7, 1.10 becomes 1.1. Opted in, only tokens that survive unchanged convert; each refusal is listed.
Examples
<user id="7"><name>Ana</name></user>{
"user": {
"@id": "7",
"name": "Ana"
}
}The attribute became @id, which cannot collide with a child named id; the report line — 1 attribute mapped to @-keys — is the engine's own sentence.
<items><item>a</item></items>{
"items": {
"item": [
"a"
]
}
}Without the always-array entry this converts to "item": "a" — code mapping over the array breaks on a one-entry feed. Forcing the name fixes the shape.
{"order": {"@status": "paid", "lines": [{"sku": "A1"}, {"sku": "B2"}]}}<?xml version="1.0" encoding="UTF-8"?>
<order status="paid">
<lines>
<sku>A1</sku>
</lines>
<lines>
<sku>B2</sku>
</lines>
</order>Going back to XML: @status became a real attribute, the two-object lines array two sibling elements. The declaration is on by default; a toggle removes it.
<id>007</id>{
"id": "007"
}Untouched by default — 007 stays text. Even with coercion on, the engine keeps it and reports: "007" kept as a string — coercing it to a number would change it.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
An attribute and a child can share a name
In <user id="7"><id>9</id></user> both survive, as @id and id, with a collision warning naming the element. Nothing is overwritten in any attribute mode.
Namespace prefixes stay in the key
Kept, soap:Body is the literal key — data["soap:Body"] in JavaScript. Stripped, it becomes Body; names that strip to one key get numbered suffixes plus a warning.
You pick what <a/> means
null by default, or "" or {}. XML cannot tell <a/> from <a></a>, so both map identically; the report counts them either way.
CDATA is literal text
Content converts exactly as written, never entity-decoded, and is counted. Converting back, reserved characters are escaped as entities, not re-wrapped in CDATA.
Custom DTD entities never expand
Only the five predefined entities and numeric references decode; a DOCTYPE-declared entity passes through as literal text, the DOCTYPE dropped and counted. Expansion bombs are harmless by construction.
Round trips hold for data-shaped XML under one preset
Every example on this page converts back to what it was — test-verified. Comments, DOCTYPE, processing instructions, inter-element whitespace and mixed-content order never survive.
RSS preset: force item into an array
A one-story feed is the single-item trap in the wild; add item — entry for Atom — before wiring a pipeline.
Sitemap preset: force url into an array
A one-page sitemap otherwise yields a lone url object where crawler tooling expects a list.
SOAP preset: keep prefixes
Envelope semantics live in the prefixes — keep them and bracket-quote the keys; strip only for quick reading.
Common errors
Expected closing tag 'title' (opened in line 1, col 7) instead of closing tag 'feed' at line 1, column 21 — probable cause: <title> is still open; close it before closing its parent.- The validator names the tag left open, the tag that tried to close, and the position, with a caret frame. XML closes inside-out — fix the first reported tag.
Unexpected '}' at line 1, column 16 — probable cause: trailing comma after the last property.- The JSON direction reuses the JSON validator, so line, column and probable cause arrive verbatim. Strict JSON permits no trailing comma — delete it and convert.
1 mixed-content region preserved as #text — review it- A review flag, not a failure: the text is kept but its position among the children is not — check that region before trusting the JSON.
Input is larger than the 50 MB limit for this tool. For bigger files use the CLI: yq -p xml -o json feed.xml- The page-side refusal: nothing is parsed, truncated or uploaded; the input pane keeps your text. yq converts XML of any size on your machine.
FAQ
Why is item an array in one file but a single object in another?
Arrays come from repetition: two <item> siblings become an array, one becomes a lone value, so a feed's shape follows its entry count. The always-array list fixes the shape, and the report counts each forced wrap.
Why do numbers and booleans stay strings?
XML text is untyped, and auto-converting is how 007 becomes 7. Nothing is coerced unless you opt in; even opted in, only values that survive unchanged convert.
Is converting XML to JSON and back lossless?
For data-shaped XML with the same preset both ways, yes in structure — these examples convert back to identical JSON. Comments, DOCTYPE, whitespace and mixed-content order have no JSON slot.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Convert again now, at any input size.
- ⌘/Ctrl + S
- Download the output — .json or .xml by direction.
- ⌘/Ctrl + C
- Copy the whole output when nothing is selected.
- ⌘/Ctrl + Shift + C
- Copy the whole output even over a selection.
- s
- Swap direction; one undo restores.
- t
- Advance the theme.
- ? or ⌘/Ctrl + /
- Open the shortcut overlay.
- Esc
- Close the topmost overlay; the panes stay.