Recently, I had a hard time figuring out how to implement a DataWeave transformation to iterate over a set of attributes within an XML element. I could not find a clear resource explaining how to achieve this, so I thought my solution might make for an interesting topic for a technical article.
The Problem
Consider the XML document below, which represents a collection of books to be sold in a bookstore. Each book element within the XML has a child element, price, which contains a set of attributes we need to iterate over.
Let’s imagine we have these requirements regarding the price element’s attributes:
- Currency: We need to replace the currency code with the currency description (e.g., USD would become US Dollars).
- Tax: We want to replace the decimal value with the text “not included” when it is not equal to 0 and with the text “included” when it is equal to 0.
- Date: We want to remove this attribute.
- Other attributes: Any other attribute present in the price element should be displayed when they exist. In our example, the shipping attribute is present just for one book.
With these requirements in mind, the desired output should look something like this:
The Solution
The first step is to understand how DataWeave’s mapObject() function works and how we can isolate the price element for all others. Assuming we know that price is a leaf node, we can construct a recursive function to traverse all XML nodes:
As the XML nodes are traversed, if the current node is an object, then it calls itself, but if it is a leaf node, it looks for the price element so we can transform it. Now that we have isolated the price formatting logic within the formatBooks() function, we need to understand how to iterate over the price element’s attributes (currency, tax, and date) and then apply the required logic for each one.
The key to solving this problem is to understand how to create a reference to all attributes. This may be accomplished with the following code:
The key.@ portion of the above expression returns back all of the attributes contained within an XML element (referred to here by key) as an Object of name/value pairs, which we may then iterate over by using DataWeave’s mapObject() function. I leveraged the key.@ operation within my formatPrice() function to isolate the price element’s attributes. I created another function, formatPriceAttrs(), which routes the processing of each attribute by using a match/case scope. The else clause permits the output of any other attribute which is present in the XML element and does not require any special handling, such as the shipping attribute in our example.
What follows is the complete DataWeave code, which you may copy into an instance of DataWeave Playground and play around with:
Well, there you have it! I hope this short technical article will be helpful to others who may be unsure as to how to get DataWeave to iterate over attributes within XML elements.