Convert XML to JSON in C#
This is the code for the options you picked (NuGet package Newtonsoft.Json). It gives the same text as the page, except that on Windows the line breaks are \r\n:
System.Text.Json has no XML support at all, so there is no built-in equivalent. For an XDocument or XElement, JsonConvert.SerializeXNode applies the same rules, but whitespace, line breaks and entities follow XDocument.Parse instead of XmlDocument.LoadXml (see below).
How XmlNodeConverter maps XML to JSON
| XML | JSON |
|---|---|
<name>Ada</name> | "name": "Ada" |
Attribute id="1" | "@id": "1", always a string |
| Text next to attributes or child elements | "#text": "..." |
<![CDATA[...]]> | "#cdata-section": "..." |
<!-- note --> | /* note */, a JavaScript comment inside the JSON |
<?xml version="1.0"?> | "?xml": { "@version": "1.0" } |
<?target data?> | "?target": "data" |
<!DOCTYPE ...> | "!DOCTYPE": { "@name": ..., "@internalSubset": ... } |
| Two or more elements with the same name | an array: "item": [ ..., ... ] |
| One element | not an array, unless it has json:Array="true" |
<a /> | "a": null |
<a></a> or <a> </a> | "a": "" |
xmlns, xmlns:p | "@xmlns", "@xmlns:p"; names keep their prefix, "p:item" |
Whitespace under xml:space="preserve" | "#significant-whitespace": " " |
Every value is a string: <qty>2</qty> becomes "2" and <paid>true</paid> becomes "true", because XML has no types. Names encoded by XmlConvert.EncodeName are decoded, so <first_x0020_name> becomes "first name".
The single element bug, and the json:Array fix
The converter decides arrays from the document it sees. An order with two items gives "item": [ ... ], an order with one item gives "item": { ... }, and a class with List<Item> Item then fails to deserialize with "Cannot deserialize the current JSON object ... into type List". Load "One item: no array" above to see it. The fix is an attribute from Newtonsoft's own namespace:
<order xmlns:json="http://james.newtonking.com/projects/json">
<item json:Array="true">Keyboard</item>
</order>
Now item is always an array, and the json:Array and xmlns:json attributes are left out of the JSON. The value is read with XmlConvert.ToBoolean, so "true" and "1" work and "yes" throws a FormatException. If you cannot change the XML, add the attribute in code before converting, or accept both shapes with a custom JsonConverter.
What XmlDocument.LoadXml does to the XML first
LoadXml reads through the old XmlTextReader, which is not the reader behind XDocument.Parse. This page copies it:
- Whitespace between elements is dropped (
PreserveWhitespaceis false), so<a> </a>is"". Underxml:space="preserve"it is kept as#significant-whitespace. - Line breaks are not normalized.
\r\nin text stays\r\n, and tabs and line breaks in attribute values stay as written.XDocumentturns them into\nand spaces. - Character references are not range checked:
�is accepted. A decimal reference such as makes whitespace significant text, while is still dropped as whitespace. - Entities declared in a DOCTYPE stay entity references in the DOM, and
SerializeXmlNodethrows "Unexpected XmlNodeType when getting node name: EntityReference". Expand them first, or load withXDocument. In attribute values they are expanded.
Options
- Formatting.Indented indents with two spaces and writes
Environment.NewLineline breaks. A comment sits right after the previous token, with no line break of its own, and a comment that contains*/is written as//lines instead. - omitRootObject leaves out the outer object and the root element's name:
<order id="1"/>becomes{"@id":"1"}. With an XML declaration, the result is not valid JSON ("?xml":{...}{...}), so remove the declaration first.
FAQ
Why is a single XML element not a JSON array?
There is no schema to say that item repeats. Add json:Array="true" as shown above, or handle both shapes when reading the JSON.
Why does my JSON contain /* ... */?
XML comments are written as JavaScript comments. Newtonsoft reads them back, but System.Text.Json rejects them unless JsonCommentHandling.Skip is set, and many other parsers reject them outright. Remove the comments first, for example with doc.SelectNodes("//comment()"). When an element has two or more comments, they collapse into an empty "#comment": [] array instead.
How do I get numbers and booleans instead of strings?
SerializeXmlNode cannot: XML text has no type. Deserialize the JSON into a class (Newtonsoft converts "2" to an int property), or skip the JSON step and read the XML into the class with XmlSerializer.
Can System.Text.Json convert XML to JSON?
No. There is no XML support in System.Text.Json. Use Newtonsoft.Json for this conversion, even in a project that otherwise uses System.Text.Json.
Is my XML uploaded?
No. The converter runs in your browser.