JSON to XML: Arrays and Attribute Conflicts
JSON has objects, arrays, strings, and numbers, but no native attributes, namespaces, or repeated sibling elements. Before converting JSON to XML, decide how arrays repeat elements, where attributes live, how text is represented, and what happens on name collisions. This article covers that mapping contract, not SOAP or CDATA.
1. Problem: one JSON shape has several XML shapes
An items array can become repeated item elements or an items container. An id can become an id child or an attribute. Without an input/output contract, a converter may emit well-formed XML that still violates the receiver's schema.
2. Minimal reproduction: mark arrays and attributes explicitly
This contract uses attributes for attributes, children for elements, and text for element text. An array repeats the same child name; other tools may choose different keys.
const input = {
name: "order",
attributes: { id: "ord-1" },
children: {
customer: { text: "Ada" },
item: [{ attributes: { sku: "book" }, text: "1" }, { attributes: { sku: "pen" }, text: "2" }],
},
};
// <order id="ord-1"><customer>Ada</customer><item sku="book">1</item><item sku="pen">2</item></order>3. Root cause: JSON has no attribute or repeated-node layer
Object keys are unique, while XML allows repeated sibling elements. A JSON key/value pair also cannot inherently say whether it is an attribute or text node. Indexing array elements into tag names changes the vocabulary; turning every field into a child may violate the receiver's contract.
4. Recommendation: write the mapping table first
For each object, define its element name, attributes, text field, and child collection. Choose repeated elements or a wrapper for arrays. If text and children can coexist, document mixed-content rules. Reject or isolate unknown fields instead of silently renaming them.
5. Complete code: create a node model from the contract
This function creates an XML node model; a serializer can then escape text and attributes. Repeated children stay repeated and array indexes never become tag names.
type XmlNode = { name: string; attributes?: Record<string, string>; text?: string; children?: XmlNode[] };
function orderToNodes(order: { id: string; customer: string; items: Array<{ sku: string; quantity: number }> }): XmlNode {
return {
name: "order",
attributes: { id: order.id },
children: [
{ name: "customer", text: order.customer },
...order.items.map((item) => ({ name: "item", attributes: { sku: item.sku }, text: String(item.quantity) })),
],
};
}
console.log(orderToNodes({ id: "ord-1", customer: "Ada", items: [{ sku: "book", quantity: 1 }] }));6. Incorrect approaches
Making item[0] and item[1] tag names makes the schema depend on data length. Giving an attribute and child the same name leaves precedence undefined. Turning an empty array into an empty string also confuses no element with one empty element.
7. Boundaries: empty arrays, names, and order
An empty array should become an empty wrapper or no element according to the contract. Repeated-element order may be meaningful and must remain stable. If a JSON field is a candidate for both an attribute and a child, reject the conflict at schema validation rather than overwriting one value.
8. Verification
Test one item, several items, an empty array, missing attributes, empty text, and values containing &<>. Assert names, repeat counts, attributes, child order, and escaping. Validate the result against the receiver's XSD or sample message instead of comparing indentation alone.
9. FAQ
Must arrays use a wrapper? No; repeated sibling elements are another valid contract.
Can duplicate JSON fields become duplicate XML nodes? A single object cannot contain duplicate keys; use an array to express repeated siblings.
Can attributes be guessed from field names? No; use an explicit attribute list and unknown-field policy.
10. Summary
The hard part of JSON-to-XML is the contract for arrays, attributes, text, and repeated names. Define the element model and conflict policy first, then generate repeated nodes and verify order, emptiness, and escaping. Do not rely on converter defaults.
Sources and further reading
Specifications and references for checking the article’s technical details.
Related articles
JSON to XML: Mapping Attributes and CDATA Explicitly
JSON has no native attribute, text, or CDATA semantics. Define explicit keys, escape XML attributes, and split the CDATA ]]> boundary before serialization.
Best practicesJSON to XML SOAP Bridge: Envelope, Body, and Namespaces
Build a controlled SOAP 1.1 Envelope from JSON-derived payload XML, covering Body, namespaces, Faults, and HTTP version boundaries without calling ordinary XML conversion a SOAP client.
How it worksJSON to TypeScript: Type Inference and Array Merging
Infer TypeScript from JSON recursively, merge object-array fields, generate safe property names, and state why a sample cannot prove a complete schema.
Keep exploring
Use the related browser tool to test the workflow with your own sample data.
Open related tool