Skip to content

XML ↔ JSON Converter — Java 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 Java implementation — the same logic the interactive tool runs, in a shareable, citable form.

// xml-to-json — bidirectional XML <-> JSON converter. Java (17) port of src/lib/xml-to-json.ts — same logic as this dir's rust.rs (Go-twin contract); JSON -> XML and entity decoding omitted for the 80-line budget (see rust.rs / python.py).
import java.io.StringReader;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.*;
import org.xml.sax.InputSource;

final class XmlJson {
    // Element -> pretty 2-space JSON written straight to `out`: attributes
    // become "@_<name>" keys, a text-only element becomes its bare string, an
    // element with attributes/children becomes an object with direct text
    // under "#text", and repeated child tags collapse into an array.
    private static void writeValue(Element el, StringBuilder out, int depth) {
        List<Element> kids = new ArrayList<>();
        StringBuilder text = new StringBuilder();
        for (Node n = el.getFirstChild(); n != null; n = n.getNextSibling()) {
            if (n instanceof Element e) kids.add(e);
            else if (n.getNodeType() == Node.TEXT_NODE || n.getNodeType() == Node.CDATA_SECTION_NODE)
                text.append(n.getTextContent()); // direct text only (skip comments/PIs)
        }
        NamedNodeMap attrs = el.getAttributes();
        String trimmed = text.toString().trim(), ind = "  ".repeat(depth + 1);
        if (kids.isEmpty() && attrs.getLength() == 0) {
            out.append('"').append(esc(trimmed)).append('"'); // bare string
            return;
        }
        out.append('{');
        Map<String, List<Element>> groups = new LinkedHashMap<>(); // document order
        for (Element k : kids)
            groups.computeIfAbsent(k.getTagName(), x -> new ArrayList<>()).add(k);
        boolean first = true;
        for (int i = 0; i < attrs.getLength(); i++) {
            Node a = attrs.item(i);
            out.append(first ? "\n" : ",\n").append(ind).append("\"@_").append(a.getNodeName())
               .append("\": \"").append(esc(a.getNodeValue())).append('"');
            first = false;
        }
        for (Map.Entry<String, List<Element>> g : groups.entrySet()) {
            out.append(first ? "\n" : ",\n").append(ind).append('"').append(esc(g.getKey())).append("\": ");
            first = false;
            List<Element> list = g.getValue();
            if (list.size() == 1) writeValue(list.get(0), out, depth + 1);
            else { // repeated tags -> array
                out.append("[\n");
                for (int j = 0; j < list.size(); j++) {
                    if (j > 0) out.append(",\n");
                    out.append("  ".repeat(depth + 2));
                    writeValue(list.get(j), out, depth + 2);
                }
                out.append('\n').append(ind).append(']');
            }
        }
        if (!trimmed.isEmpty())
            out.append(first ? "\n" : ",\n").append(ind).append("\"#text\": \"").append(esc(trimmed)).append('"');
        out.append('\n').append("  ".repeat(depth)).append('}');
    }

    private static String esc(String s) {
        return s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n")
                .replace("\r", "\\r").replace("\t", "\\t");
    }

    // Parse well-formed XML to pretty JSON, or null if the DOM parser rejects
    // it (mismatched tags, no root, trailing junk all throw here).
    static String xmlToJson(String xml) {
        try {
            Element root = DocumentBuilderFactory.newInstance().newDocumentBuilder()
                .parse(new InputSource(new StringReader(xml))).getDocumentElement();
            StringBuilder out = new StringBuilder("{\n  \"").append(esc(root.getTagName())).append("\": ");
            writeValue(root, out, 1);
            return out.append("\n}").toString();
        } catch (Exception e) {
            return null;
        }
    }

    public static void main(String[] args) {
        System.out.println(xmlToJson("<user id=\"7\"><name>Alice</name><pet>cat</pet><pet>dog</pet></user>"));
        System.out.println(xmlToJson("<a><b></a>")); // mismatched close -> null
    }
}

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 →