JSON to XML: Mapping Attributes and CDATA Explicitly
A JSON string carries characters but not an XML attribute or CDATA marker. A converter therefore needs an input convention or intermediate model; otherwise placement in an attribute, normal text, or CDATA depends on defaults. This article covers attributes and CDATA, not array conflicts or SOAP.
1. Problem: string content is not one XML representation
The value a < b can be escaped as ordinary XML text or placed in CDATA, while an id may need to be an attribute. The displayed characters can match even though schema types and receiver expectations differ.
2. Minimal reproduction: reserve explicit attribute and CDATA keys
This contract uses attributes for attributes, text for ordinary text, and cdata for CDATA. A node chooses either text or $cdata; attributes always use XML attribute escaping.
const input = {
message: {
$attributes: { id: "m-1" },
$cdata: "if (a < b && c > d) return;",
},
};
// <message id="m-1"><![CDATA[if (a < b && c > d) return;]]></message>3. Root cause: CDATA is XML syntax, not a JSON type
JSON.parse and JSON.stringify preserve only a string, not the intention that it was CDATA. Attributes are also not a native JSON layer. Introduce an explicit convention at the boundary and reject a node that declares both ordinary text and CDATA.
4. Recommendation: normalize into an explicit intermediate model
Normalize each node into name, attributes, text, or cdata before calling an XML serializer. Escape ampersands and angle brackets in ordinary text, and quotes in attributes. CDATA cannot contain ]]>; split that sequence instead of emitting invalid XML.
5. Complete code: escape attributes and split CDATA
This code returns an XML content fragment and demonstrates attribute escaping and CDATA splitting. It does not implement namespaces, arrays, or a SOAP envelope.
function escapeAttribute(value: string): string {
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll("\"", """);
}
function wrapCdata(value: string): string {
return "<![CDATA[" + value.replaceAll("]]>", "]]]]><![CDATA[>") + "]" + "]>";
}
function messageXml(id: string, script: string): string {
return "<message id=\"" + escapeAttribute(id) + "\">" + wrapCdata(script) + "</message>";
}
console.log(messageXml("m&1", "if (a < b && c > d) return;"));6. Incorrect approaches
Wrapping every string in CDATA changes an ordinary-text contract. Treating $cdata as an attribute produces the wrong layer, and emitting ]]> directly terminates CDATA. Escaping < without escaping & can also create malformed or differently interpreted text.
7. Boundaries: mixed content, empty values, and controls
An empty string may mean an empty element, empty CDATA, or an absent field; define the distinction. An element with text and children is mixed content and should not be reordered by a generic object walker. Reject forbidden XML control characters before serialization.
8. Verification
Test &, angle brackets, quotes, ]]> inside CDATA, empty strings, newlines, Unicode, and control characters. Parse the serialized document with an XML parser, compare attribute/text/CDATA characters, and confirm the receiver schema permits that content.
9. FAQ
Can CDATA contain any string? No; ]]> must be split, and XML still disallows some control characters.
Does CDATA have a different parsed value? The characters can be the same, but serialization and receiver contracts may differ.
Can JSON recover original attributes? No; attribute intent must come from an input convention or external schema.
10. Summary
JSON has no attribute or CDATA type, so use an explicit key convention or intermediate model. Escape attributes by XML rules, choose text versus CDATA deliberately, and verify ]]> boundaries, controls, and mixed content.
Sources and further reading
Specifications and references for checking the article’s technical details.
Related articles
JSON to XML: Arrays and Attribute Conflicts
Define array, attribute, text, and repeated-element rules before mapping JSON to XML, with an order example that keeps the input/output contract explicit.
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 worksMyers Diff for JSON Arrays: Limits and Implementation
Use Myers shortest edit scripts for ordered JSON arrays, with insert/delete/equal operations, path patches, and clear limits around object identity and duplicate values.
Keep exploring
Use the related browser tool to test the workflow with your own sample data.
Open related tool