XML ↔ JSON Converter — C# source
Convert XML to JSON and back, preserving attributes. Validates input and reports errors clearly, runs entirely in your browser, with a shareable link to your exact input.
This is the C# implementation — the same logic the interactive tool runs, in a shareable, citable form.
// xml-to-json — bidirectional XML <-> JSON converter. C# (.NET 8) port of src/lib/xml-to-json.ts — same logic as this dir's rust.rs (Go-twin contract); XML entity edge cases simplified for the 80-line budget (see rust.rs / python.py).
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Xml.Linq;
// XElement -> JsonNode: attributes become "@_<name>" keys, a text-only element becomes
// its bare string, an element with attributes/children becomes an object whose direct
// text lands under "#text", repeated tags -> an array.
static JsonNode? ElementValue(XElement el)
{
var attrs = el.Attributes().ToList();
var kids = el.Elements().ToList();
if (kids.Count == 0 && attrs.Count == 0)
return (string?)el; // text-only -> bare string
var obj = new JsonObject();
attrs.ForEach(a => obj[$"@_{a.Name}"] = (string?)a);
foreach (var g in kids.GroupBy(e => e.Name.LocalName)) // first-occurrence order
obj[g.Key] = g.Count() == 1
? ElementValue(g.First())
: new JsonArray(g.Select(ElementValue).ToArray());
var text = el.Value.Trim();
if (text.Length > 0) obj["#text"] = text;
return obj;
}
// Parse well-formed XML to pretty 2-space JSON, or null if XDocument rejects it
// (mismatched tags, no root, trailing junk all throw here).
string? XmlToJson(string xml)
{
try
{
var root = XDocument.Parse(xml).Root!;
return new JsonObject { [root.Name.LocalName] = ElementValue(root) }
.ToJsonString(new JsonSerializerOptions { WriteIndented = true });
}
catch { return null; }
}
// Build indented XML from a JSON string with an object root, or null if invalid.
string? JsonToXml(string json)
{
JsonNode? root;
try { root = JsonNode.Parse(json); }
catch { return null; }
if (root is not JsonObject obj) return null;
var sb = new StringBuilder();
foreach (var (k, v) in obj) Walk(sb, k, v, 0);
return sb.ToString();
void Walk(StringBuilder sb, string tag, JsonNode? v, int depth)
{
var ind = new string(' ', depth * 2);
if (v is JsonObject o)
{
var attrs = new StringBuilder();
var text = "";
var kids = new List<(string K, JsonNode? V)>();
foreach (var (k, val) in o)
{
if (k.StartsWith("@_")) attrs.Append($" {k[2..]}=\"{val}\"");
else if (k == "#text") text = val?.ToString() ?? "";
else kids.Add((k, val));
}
sb.Append($"{ind}<{tag}{attrs}>");
if (kids.Count == 0) { sb.AppendLine(text + $"</{tag}>"); return; }
sb.AppendLine();
foreach (var (k, val) in kids)
if (val is JsonArray arr) foreach (var item in arr) Walk(sb, k, item, depth + 1);
else Walk(sb, k, val, depth + 1);
sb.Append($"{ind}</{tag}>\n");
}
else sb.Append($"{ind}<{tag}>{v}</{tag}>\n"); // bare text value
}
}
// Demo: parse, attributes, repeated tags, both directions, malformed input.
var xml = "<user id=\"7\"><name>Alice</name><pet>cat</pet><pet>dog</pet></user>";
Console.WriteLine(XmlToJson(xml));
Console.WriteLine(JsonToXml("{\"user\":{\"@_id\":\"7\",\"name\":\"Alice\"}}"));
Console.WriteLine(XmlToJson("<a><b></a>") ?? "null"); // mismatched close
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →