XML to JSON: Preserving Attribute Metadata
Automated XML-to-JSON conversion often struggles to represent XML
attributes alongside element text without cluttering the resulting JSON
with prefixed keys like @ or _. This article
examines the structural mismatch between XML and JSON, outlining four
practical strategies—metadata wrapper objects, JSON-LD context mapping,
sidecar metadata structures, and non-enumerable runtime properties—that
automated converters can use to preserve attribute fidelity without
polluting core data payloads.
The Impedance Mismatch
XML distinguishes between element tags, character data, and attributes attached to opening tags. Conversely, JSON relies strictly on unordered key-value pairs, arrays, and primitive types.
When a standard converter encounters an element with both text
content and attributes (e.g.,
<price currency="USD">29.99</price>),
conventional algorithms like BadgerFish or Parker either flatten the
structure and discard attributes, or introduce synthetic keys such as
@currency and $text. This approach pollutes
JSON object keys, breaking standard schema validation models,
complicating deserialization in strongly-typed languages, and degrading
API ergonomics.
Strategy 1: Dedicated Metadata Sub-Objects
Rather than mixing attribute keys directly alongside child elements
in the same object scope, converters can isolate attributes inside a
single reserved namespace property (such as _attributes,
$meta, or __meta__).
{
"price": {
"value": 29.99,
"_attributes": {
"currency": "USD",
"id": "p-101"
}
}
}This method confines metadata pollution to a single predictable property. Consuming clients can easily extract the core value or process business properties without having to filter out various dynamic attribute names across every level of the object hierarchy.
Strategy 2: Sidecar Metadata Generation
For systems requiring completely pure, idiomatic JSON domain models, automated converters can output a dual-structure payload: the normalized JSON data payload alongside a parallel “sidecar” metadata tree.
{
"data": {
"product": {
"price": 29.99
}
},
"metadata": {
"paths": {
"product.price": {
"attributes": {
"currency": "USD"
}
}
}
}
}By decoupling the attribute data into a path-indexed dictionary or
matching mirror tree, data consumption pipelines can consume
data directly into native model objects without mapping
exceptions, while metadata-aware consumers reference the sidecar
structure when auditing, tracing, or serializing back to XML.
Strategy 3: Semantic Linked Data Contexts (JSON-LD)
Converters can leverage JSON-LD (JavaScript Object Notation for Linked Data) specifications to map XML attributes to semantic contexts without changing the primary key structure.
Attributes are mapped into a root-level @context
definition or associated URI definitions. Metadata such as data types,
units of measure, or localization attributes are bound to property terms
within the context block. The actual data payload remains clean and
straightforward, while remaining fully reconstructible into XML using
the context dictionary.
Strategy 4: Non-Enumerable In-Memory Properties
In JavaScript/TypeScript or dynamic runtime conversion environments,
automated parsers can attach attributes to JSON objects as
non-enumerable properties or using dynamic Symbol
identifiers.
const priceObj = { value: 29.99 };
Object.defineProperty(priceObj, Symbol.for('attributes'), {
value: { currency: 'USD' },
enumerable: false,
writable: false
});Because these properties are non-enumerable, standard operations like
Object.keys(), for...in loops, and standard
JSON.stringify() calls process only the core business keys.
When metadata is needed for two-way transformation, specialized
serialization handlers read the symbol or hidden descriptor to
reconstruct the original XML attributes accurately.