Mastering DataWeave: The Ultimate Guide to Clean, Organized XML

Let’s face it: XML data can get messy. Between attributes appearing in random orders, numbers cluttering up with leading zeros, and empty tags taking up unnecessary space, your payloads can quickly become difficult to read and heavy to process.

If you are working with MuleSoft, you already have the solution in your pocket: DataWeave (DWL).

In this guide, we are going to look at a specific, high-utility script designed to deep-clean your XML. Think of this as the "Marie Kondo" for your data—keeping what sparks joy (data integrity) and tossing what doesn't (empty tags and formatting errors).

What This Script Does

Before we dive into the code, let's understand the three specific problems we are solving. This script performs a triple-threat optimization:

  • Orders Attributes: It forces attributes within your tags to line up alphabetically. No more hunting for keys; everything is exactly where you expect it to be.
  • Formats Numbers: It detects numeric values trapped in strings (like "00123") and strips away the unnecessary leading zeros (turning it into "123").
  • Removes Empty Tags: It sweeps through the document and removes any tags that don't contain data, reducing your file size and noise.

The Solution: The DataWeave Script

Below is the complete, ready-to-run DWL script. Copy this into your transform component.

Note: This script uses strict type checking and recursive functions to ensure it hits every level of your XML tree.

%dw 2.0
import * from dw::Runtime

fun isItNumber(num) =
  (num match {
    case item is Number -> item as Number
    case item is String -> (try(() -> item as Number) orElseTry (item replace /^0+/ with "")).result
    else -> $
  })

fun treeOrderAttributes(value: Any, predicate: (value: Any) -> Boolean) =
  value match {
    case object is Object -> do {
      object mapObject ((value, key, index) -> 
        (key) @(((key.@ orderBy (value, key) -> key) mapObject ((atValue, atKey, atIndex) -> 
          (atKey): isItNumber(atValue)
        ))): treeOrderAttributes(value, predicate))
      ) filterObject ((value, key, index) -> predicate(value) and not (value is String and isEmpty(value)))
    }
    case array is Array -> do {
      array map ((item, index) -> treeOrderAttributes(item, index)) filter ((item, index) -> predicate(item))
    }
    else -> isItNumber($)
  }

fun orderAttributes(value: Any) =
  treeOrderAttributes(value, (value) -> value match {
      case v is Array | Object | Null | "" -> !isEmpty(v)
      else -> true
    })
output application/xml  indent=false
---
orderAttributes(payload)

How It Works: Under the Hood

You don't need to be a wizard to use this, but understanding the logic helps. Here is the breakdown of the functions used above:

1. The isItNumber Function

This is the "checker." It looks at a value to see if it behaves like a number.

  • If it is a number, it keeps it.
  • If it is a string that looks like a number (e.g., "007"), it strips the zeros.

Why this matters: It ensures numerical consistency without breaking non-numeric strings.

2. The treeOrderAttributes Function

This is the "engine." It uses recursion to walk through every branch of your XML tree.

  • Recursive Traversal: It goes deep into objects and arrays.
  • Attribute Sorting: It grabs the attributes (key.@) and runs an orderBy command to sort them alphabetically.
  • Filtration: It applies the logic to remove empty objects or strings.

3. The orderAttributes Function

This is the "trigger." It kicks off the process and defines the specific rule for what counts as "empty" (nulls, empty strings, empty arrays).

Real-World Example

Let's see this in action. Below is a raw XML input that is messy, unsorted, and contains zeros.

Input XML

<?xml version='1.0' encoding='UTF-8'?><root>
  <child1 attribute11="value65" attribute44="0.120" attribute72="0002" attribute40="value39" attribute75="value55">This is child 1
    <subchild1 attribute37="value17">00123</subchild1>
  </child1>
  <child2 attribute48="value62">0.410
    <subchild2 attribute53="-0.3100" attribute11="-2431.03">This is subchild 2</subchild2>
  </child2></root>

Output XML (After Transformation)

Notice that attribute40 now comes before attribute44, 0002 became 2, and the structure is clean.

<?xml version='1.0' encoding='UTF-8'?><root>
  <child1 attribute11="value65" attribute40="value39" attribute44="0.12" attribute72="2" attribute75="value55">This is child 1
    <subchild1 attribute37="value17">123</subchild1>
  </child1>
  <child2 attribute48="value62">.410
    <subchild2 attribute11="-2431.03" attribute53="-0.31">This is subchild 2</subchild2>
  </child2></root>

Why This Matters for Your Project

By implementing this optimization script, you gain three distinct advantages:

  • Readability: Debugging becomes significantly easier when attributes follow a predictable, alphabetical order.
  • Performance: Removing empty tags and formatting numbers reduces the payload size, leading to faster parsing and transmission.
  • Data Integrity: You eliminate ambiguity in your data (e.g., 001 vs 1) ensuring downstream systems process the values correctly.


Copy the script, test it in your playground, and enjoy cleaner XML!

Post a Comment (0)
Previous Post Next Post