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.