Skip to content

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 →