DEV Community

Cover image for The DataWeave <~ Operator: How Angle Brackets in Customer Notes Broke 200 Orders
ThaSha
ThaSha

Posted on

The DataWeave <~ Operator: How Angle Brackets in Customer Notes Broke 200 Orders

A customer typed "" in their notes field. It broke 200 XML orders and I spent 3 hours finding a one-line fix.

TL;DR

  • User-generated text with angle brackets (<, >) breaks XML output — the parser treats them as tags
  • The <~ metadata operator with {cdata: true} wraps content in CDATA sections
  • CDATA preserves angle brackets as literal text: <![CDATA[text with <VIP>]]>
  • The <~ operator requires DataWeave 2.5 (Mule 4.5+)
  • JSON output silently ignores cdata metadata — no error, no effect

The Problem: Valid JSON, Invalid XML

We had a Mule flow converting customer orders from JSON to XML for an ERP. Worked for 6 months. Then 200 orders got rejected.

The customer's notes field:

{
  "customer": {
    "name": "Alice Chen",
    "email": "alice@example.com",
    "notes": "Preferred customer since 2020 <VIP>"
  },
  "orderId": "ORD-12345"
}
Enter fullscreen mode Exit fullscreen mode

The DataWeave transform:

%dw 2.0
output application/xml
---
order @(id: payload.orderId): {
    customer @(class: "Person", source: "CRM"): {
        name: payload.customer.name,
        email: payload.customer.email,
        notes: payload.customer.notes
    }
}
Enter fullscreen mode Exit fullscreen mode

Output XML:

<order id="ORD-12345">
  <customer class="Person" source="CRM">
    <name>Alice Chen</name>
    <notes>Preferred customer since 2020 <VIP></notes>
  </customer>
</order>
Enter fullscreen mode Exit fullscreen mode

The <VIP> in the notes became an XML opening tag. No closing </VIP> tag. Invalid XML. The ERP rejected the entire batch.


100 production-ready DataWeave patterns with tests: mulesoft-cookbook on GitHub


The Fix: The <~ Metadata Operator

%dw 2.0
output application/xml
---
order @(id: payload.orderId): {
    customer @(class: "Person", source: "CRM"): {
        name: payload.customer.name,
        email: payload.customer.email,
        notes: payload.customer.notes <~ {cdata: true}
    }
}
Enter fullscreen mode Exit fullscreen mode

Output:

<order id="ORD-12345">
  <customer class="Person" source="CRM">
    <name>Alice Chen</name>
    <notes><![CDATA[Preferred customer since 2020 <VIP>]]></notes>
  </customer>
</order>
Enter fullscreen mode Exit fullscreen mode

The <~ operator attaches metadata to a value without changing its type. {cdata: true} tells the XML writer to wrap the content in a CDATA section. Angle brackets preserved. Valid XML.

The @() Syntax for XML Attributes

The @() syntax adds XML attributes to elements:

customer @(class: "Person", source: "CRM"): { ... }
Enter fullscreen mode Exit fullscreen mode

Produces: <customer class="Person" source="CRM">. Attributes come from your data, not hardcoded in the schema.

Trap 1: <~ Only Works for XML Output

I tried using <~ {cdata: true} on a JSON API response. Nothing happened. No error. No warning. The output was identical with and without the <~.

The cdata metadata is only read by the XML writer. JSON, CSV, and other formats ignore it silently.

Trap 2: Requires DataWeave 2.5

The <~ operator was introduced in DataWeave 2.5 (Mule 4.5+). On older runtimes, it throws a parse error.

Before <~, the approach was:

notes: payload.customer.notes as String {class: "cdata"}
Enter fullscreen mode Exit fullscreen mode

This works on older runtimes but also coerces the type. <~ is cleaner because it only attaches metadata — the value type stays unchanged.

What I Do Now

Any field going to XML that could contain user-generated content gets <~ {cdata: true}:

  • Customer notes
  • Product descriptions
  • Address fields (street addresses with unit symbols like #)
  • Comment fields from ticketing systems

I also run a pre-transformation check: scan text fields for <, >, &. If found, log a warning and ensure CDATA wrapping is active.


100 patterns with MUnit tests: github.com/shakarbisetty/mulesoft-cookbook

60-second video walkthroughs: youtube.com/@SanThaParv

Top comments (0)