From Json.NET documentation: http://james.newtonking.com/projects/json/help/?topic=html/ConvertingJSONandXML.htm
You can force a node to be rendered as an Array by adding the attribute json:Array='true' to the XML node you are converting to JSON. Also, you need to declare the json prefix namespace at the XML header xmlns:json='http://james.newtonking.com/projects/json' or else you will get an XML error stating that the json prefix is not declared.
The next example is provided by the documentation:
xml = @"<person xmlns:json='http://james.newtonking.com/projects/json' id='1'>
<name>Alan</name>
<url>http://www.google.com</url>
<role json:Array='true'>Admin</role>
</person>";
Generated output:
{
"person": {
"@id": "1",
"name": "Alan",
"url": "http://www.google.com",
"role": [
"Admin"
]
}
}
Answer from Iván Pérez Gómez on Stack OverflowFrom Json.NET documentation: http://james.newtonking.com/projects/json/help/?topic=html/ConvertingJSONandXML.htm
You can force a node to be rendered as an Array by adding the attribute json:Array='true' to the XML node you are converting to JSON. Also, you need to declare the json prefix namespace at the XML header xmlns:json='http://james.newtonking.com/projects/json' or else you will get an XML error stating that the json prefix is not declared.
The next example is provided by the documentation:
xml = @"<person xmlns:json='http://james.newtonking.com/projects/json' id='1'>
<name>Alan</name>
<url>http://www.google.com</url>
<role json:Array='true'>Admin</role>
</person>";
Generated output:
{
"person": {
"@id": "1",
"name": "Alan",
"url": "http://www.google.com",
"role": [
"Admin"
]
}
}
Giving my +1 to Iván Pérez Gómez and providing some code here to support his answer:
Add the required json.net namespace to the root node:
private static void AddJsonNetRootAttribute(XmlDocument xmlD)
{
XmlAttribute jsonNS = xmlD.CreateAttribute("xmlns", "json", "http://www.w3.org/2000/xmlns/");
jsonNS.Value = "http://james.newtonking.com/projects/json";
xmlD.DocumentElement.SetAttributeNode(jsonNS);
}
And to add json:Array attribute to elements found by xpath:
private static void AddJsonArrayAttributesForXPath(string xpath, XmlDocument doc)
{
var elements = doc.SelectNodes(xpath);
foreach (var element in elements)
{
var el = element as XmlElement;
if (el != null)
{
var jsonArray = doc.CreateAttribute("json", "Array", "http://james.newtonking.com/projects/json");
jsonArray.Value = "true";
el.SetAttributeNode(jsonArray);
}
}
}
Here is a sample of a single child node as a json array:

Read this documentation about Serialize Xml Node
You can force JSON Array this way
var xml = @"<Items xmlns:json='http://james.newtonking.com/projects/json' >
<Item json:Array='true'>
<Name>name</Name>
<Detail>detail</Detail>
</Item>
</Items>";
DEMO
In case it helps anyone, further to meda's reply. Here's how you make this work with XElement rather than xmlTextWriter and XDocument
XNamespace ns = "http://james.newtonking.com/projects/json";
var items = new XElement("items",new XAttribute(XNamespace.Xmlns+"json",ns));
items.Add(new XElement("item",new XAttribute(ns+"Array",true),
new XElement("name", "name"),
new XElement("Detail", "detail")));
then to convert it
XmlDocument doc = new XmlDocument();
doc.LoadXml(items.ToString());
var converted JsonConvert.SerializeXmlNode(doc);
If you know XML schema beforehand you can force array generation by attaching json:Array="true" to the node you want to convert to an array
static string convertToJson(string what)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(what);
var products = doc.GetElementsByTagName("Product");
if (products.Count == 1)
{
var attribute = doc.CreateAttribute("json", "Array", "http://james.newtonking.com/projects/json");
attribute.InnerText = "true";
var node = products.Item(0) as XmlElement;
node.Attributes.Append(attribute);
}
string json = JsonConvert.SerializeXmlNode(doc);
return json;
}
It shouldn't impact too much in your performance if you create an array of objects in your code from your XML doc (you need to know the content structure and use POCO objects) and then serialize that list with json. Makes it sense in your case?
It seems that the json prefix is getting stripped off the json:Array attribute when you add it to the node, since it is not qualified with a namespace. Without the json prefix, the attribute has no special meaning to Json.Net; thus, it gets written into the JSON instead of changing the output behavior.
Try it like this instead:
string xml =
@"<person>
<name>Joe</name>
<age>28</age>
<role>Admin</role>
</person>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
foreach (XmlNode node in doc.DocumentElement.ChildNodes)
{
if (node.Name == "role")
{
XmlAttribute att = doc.CreateAttribute("json", "Array", "http://james.newtonking.com/projects/json");
att.Value = "true";
node.Attributes.Append(att);
}
}
string json = JsonConvert.SerializeXmlNode(doc);
Console.WriteLine(json);
Output:
{
"person": {
"name": "Joe",
"age": "28",
"role": [
"Admin"
]
}
}
So I figured out what I was doing wrong if anyone is interested. When I was creating the attribute:
XmlAttribute attr = originalDoc.CreateAttribute(@"json:Array");
I was trying to name it with my prefix. The proper way to do this is to pass in the prefix, name and namespace as the parameters.
XmlAttribute attr = doc.CreateAttribute("json", "Array", "http://james.newtonking.com/projects/json");
There is overload of DeserializeXmlNode which accepts boolean flag named writeArrayAttribute. That's what you need:
XmlDocument xml = JsonConvert.DeserializeXmlNode(json, null, true);
Produces:
<person id="1">
<name>Alan</name>
<url>http://www.google.com</url>
<role xmlns:json="http://james.newtonking.com/projects/json" json:Array="true">Admin</role>
</person>
Which is semantically identical to original xml.
The XMl to JSon loses all the information of any attributes that have ':' (colon) in their name. Which is why 'id' get serialised to @id but 'xmlns:json' is lost in translation.
If you have access to the raw XML then I will suggest you replace the colons(:) by hyphens(-). In this case, the XML will be:
<person xmlns-json='http://james.newtonking.com/projects/json' id='1'>
<name>Alan</name>
<url>http://www.google.com</url>
<role json-Array='true'>Admin</role>
</person>
I have checked that this serialises and de-serialises to the same input and output.
var xmlString = @"<person xmlns-json='http://james.newtonking.com/projects/json' id='1'><name>Alan</name><url>http://www.google.com</url><role json-Array='true'>Admin</role></person>";
var xml = new XmlDocument();
xml.LoadXml(xmlString);
var json = JsonConvert.SerializeXmlNode(xml);
var xmlDeserialized = JsonConvert.DeserializeXmlNode(json);
xmlDeserialized.Should().NotBeNull();
xmlDeserialized.ShouldBeEquivalentTo(xml); //All good
Yes. Using the JsonConvert class which contains helper methods for this precise purpose:
// To convert an XML node contained in string xml into a JSON string
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
string jsonText = JsonConvert.SerializeXmlNode(doc);
// To convert JSON text contained in string json into an XML node
XmlDocument doc = JsonConvert.DeserializeXmlNode(json);
Documentation here: Converting between JSON and XML with Json.NET
Yes, you can do it (I do) but Be aware of some paradoxes when converting, and handle appropriately. You cannot automatically conform to all interface possibilities, and there is limited built-in support in controlling the conversion- many JSON structures and values cannot automatically be converted both ways. Keep in mind I am using the default settings with Newtonsoft JSON library and MS XML library, so your mileage may vary:
XML -> JSON
- All data becomes string data (for example you will always get "false" not false or "0" not 0) Obviously JavaScript treats these differently in certain cases.
- Children elements can become nested-object
{}OR nested-array[ {} {} ...]depending if there is only one or more than one XML child-element. You would consume these two differently in JavaScript, etc. Different examples of XML conforming to the same schema can produce actually different JSON structures this way. You can add the attribute json:Array='true' to your element to workaround this in some (but not necessarily all) cases. - Your XML must be fairly well-formed, I have noticed it doesn't need to perfectly conform to W3C standard, but 1. you must have a root element and 2. you cannot start element names with numbers are two of the enforced XML standards I have found when using Newtonsoft and MS libraries.
- In older versions, Blank elements do not convert to JSON. They are ignored. A blank element does not become "element":null
A new update changes how null can be handled (Thanks to Jon Story for pointing it out): https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_NullValueHandling.htm
JSON -> XML
- You need a top level object that will convert to a root XML element or the parser will fail.
- Your object names cannot start with a number, as they cannot be converted to elements (XML is technically even more strict than this) but I can 'get away' with breaking some of the other element naming rules.
Please feel free to mention any other issues you have noticed, I have developed my own custom routines for preparing and cleaning the strings as I convert back and forth. Your situation may or may not call for prep/cleanup. As StaxMan mentions, your situation may actually require that you convert between objects...this could entail appropriate interfaces and a bunch of case statements/etc to handle the caveats I mention above.
How about this?
This assumes that the array of JSON objects all have the same keys.
let xml = "";
const prefixXML = `<?xml version="1.0" encoding="UTF-8"?>\n<cars>\n`;
const suffixXML = "\n</cars>";
const keys = Object.keys(cars[0]);
cars.forEach((car) => {
let valueXML = keys
.map((key) => {
return `<${key}>${car[key]}</${key}>`;
})
.join("\n\t");
xml += `
<car>
${valueXML}
</car>
`;
});
const final = prefixXML + xml + suffixXML;
console.log(final);
The XML output is:
<?xml version="1.0" encoding="UTF-8"?>
<cars>
<car>
<color>purple</color>
<type>minivan</type>
<registration>2020-02-03</registration>
<capacity>7</capacity>
</car>
<car>
<color>orange</color>
<type>SUV</type>
<registration>2021-05-17</registration>
<capacity>4</capacity>
</car>
<car>
<color>green</color>
<type>coupe</type>
<registration>2019-11-13</registration>
<capacity>2</capacity>
</car>
</cars>
If you're using FXP (fast-xml-parser), you can use folloing configuration to avoid array index as tag name
const parser = new j2xParser({
rootNodeName: "car"
});
const result = parser.parse(cars);
FXP version must be > 3.21.0
DeserializeXmlNode() doesn't really have a way to customize the way it does its JSON to XML conversion. To get the result you want using that method, you will either have to manipulate the JSON before converting it to XML, or manipulate the XML afterwards.
In this case, I think it might be easier to use Json.Net's LINQ-to-JSON API to build the XML directly from the JSON in the shape you want. You can do it like this:
var ja = JArray.Parse(jsonResult);
var xml = new XDocument(
new XElement("cars",
ja.Select(c =>
new XElement("car",
new XElement("features",
c["car"]["features"].Select(f =>
new XElement("feature",
new XElement("code", (string)f["code"])
)
)
)
)
)
)
);
Console.WriteLine(xml.ToString());
Fiddle: https://dotnetfiddle.net/fxxQnL
With Cinchoo ETL - an open source library, you can do the Xml to Json easily with few lines of code
string json = @"
[
{
""car"": {
""features"": [{
""code"": ""1""
}, {
""code"": ""2""
}]
}
},
{
""car"": {
""features"": [{
""code"": ""3""
}, {
""code"": ""2""
}]
}
}
]";
StringBuilder sb = new StringBuilder();
using (var p = ChoJSONReader.LoadText(json))
{
using (var w = new ChoXmlWriter(sb)
.Configure(c => c.RootName = "cars")
//.Configure(c => c.IgnoreRootName = true)
.Configure(c => c.IgnoreNodeName = true)
)
{
w.Write(p);
}
}
Console.WriteLine(sb.ToString());
Output:
<cars>
<car>
<features>
<feature>
<code>1</code>
</feature>
<feature>
<code>2</code>
</feature>
</features>
</car>
<car>
<features>
<feature>
<code>3</code>
</feature>
<feature>
<code>2</code>
</feature>
</features>
</car>
</cars>
Checkout CodeProject article for some additional help.
Disclaimer: I'm the author of this library.
There is an underscore-java library with static method U.xmlToJson(xml). It supports a special attribute array="true" which forces the element to be an array.
<project id="200">
<name>test</name>
<elements>
<element array="true">
<id>body</id>
<width>200</width>
<height>400</height>
<children/>
</element>
</elements>
</project>
Output:
{
"project": {
"-id": "200",
"name": "test",
"elements": [
{
"id": "body",
"width": "200",
"height": "400",
"children": {
"-self-closing": "true"
}
}
]
},
"#omit-xml-declaration": "yes"
}
Answering your 1st question: What is the proper way to represent an array in xml?
Please refere to: http://www.w3.org/2005/07/xml-schema-patterns.html#Vector
replace your OBJtoXML function with
function OBJtoXML(obj) {
var xml = '';
for (var prop in obj) {
xml += obj[prop] instanceof Array ? '' : "<" + prop + ">";
if (obj[prop] instanceof Array) {
for (var array in obj[prop]) {
xml += "<" + prop + ">";
xml += OBJtoXML(new Object(obj[prop][array]));
xml += "</" + prop + ">";
}
} else if (typeof obj[prop] == "object") {
xml += OBJtoXML(new Object(obj[prop]));
} else {
xml += obj[prop];
}
xml += obj[prop] instanceof Array ? '' : "</" + prop + ">";
}
var xml = xml.replace(/<\/?[0-9]{1,}>/g, '');
return xml
}
Using xml-js lib
import { json2xml } from "xml-js";
const input = {
contact: {
name: `John & cia "example"`
}
};
const xml = json2xml(input, {
compact: true
});
// <contact><name>John & cia \"example\"</name></contact>
https://codesandbox.io/s/xml-json-forked-zgit4?file=/src/index.js:97-103
:)