How to avoid repetition in DataWeave for conditional checks?

Last Updated on 30/05/2021 by Patryk Bandurski

Tip number 1 will be about data existence check. There is often a situation that nearly the same conditions need to be checked in every line. I have seen many transformations that were really long and complex. Reading them was not only difficult but a lot of repeatable conditional checks were made. Here I will show you an example that will evolve to the point where we reuse everything that was possible. As a result, we should achieve a more concise and readable transformation.

Here is the input in XML format and expected JSON result:

Input – application/xml content type

<?xml version="1.0" encoding="UTF-8"?>
<catalog>
	<product description="Cardigan Sweater"
       		product_image="cardigan.jpg">
		<catalog_item gender="Men's">
			<item_number>QWZ5671</item_number>
			<price>39.95</price>
			<size description="Medium">
				<color_swatch image="red_cardigan.jpg">Red</color_swatch>
				<color_swatch image="burgundy_cardigan.jpg">Burgundy</color_swatch>
			</size>
			<size description="Large">
				<color_swatch image="red_cardigan.jpg">Red</color_swatch>
				<color_swatch image="burgundy_cardigan.jpg">Burgundy</color_swatch>
			</size>
		</catalog_item>
		<catalog_item gender="Women's">
		</catalog_item>
	</product>
</catalog>

Output – application/json content type

{
	"Product": {
		"Man": {
			"Id": "QWZ5671",
			"Sizes": 2,
			"Price": 39
		},
		"Woman": {}
	}
}

Failing transformation due to null values

Below I have prepared transformation without checking if data is either provided or not. For a given input, my transformation will not work. DataWeave can not apply cast operator as on null value. This transformation would work perfectly fine only for input where all data is present.

%dw 2.0
output application/json skipNullOn="everywhere"
---
{
   Product: {
      Man: {
         Id: payload.catalog.product.*catalog_item[0].item_number,
         s: payload.catalog.product.*catalog_item[2].size.color_swatch,
         Sizes: sizeOf(payload.catalog.product.*catalog_item[0].*size as Array),
         Price: floor(payload.catalog.product.*catalog_item[0].price as Number * 100),
      },
      Woman: {
         Id: payload.catalog.product.*catalog_item[1].item_number,
         Sizes: sizeOf(payload.catalog.product.*catalog_item[1].*size as Array),
         Price: floor(payload.catalog.product.*catalog_item[1].price as Number * 100),
      }
   }
}

Excecution error should be simillar to the one below:

14| Sizes: sizeOf(payload.catalog.product.*catalog_item[1].*size as Array), 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
Cannot coerce Null (null) to Array 
Trace: 
  at 'sizeOf' in (anonymous:14:11) 
  at 'main' in (anonymous:4:1)

Let’s fix it.

Default value

The easiest approach is to use the default operator. The construction looks as follows.

default operator

After the keyword default we put value that DataWeave engine will return if parameter turns out to be empty. In transformation below you may notice that *size is set to empty array and price is set to 0 only if corresponding parameters are empty.

%dw 2.0
output application/json skipNullOn="everywhere"
---
{
   Product: {
      Man: {
         Id: payload.catalog.product.*catalog_item[0].item_number,
         s: payload.catalog.product.*catalog_item[2].size.color_swatch,
         Sizes: sizeOf(payload.catalog.product.*catalog_item[0].*size as Array default []),
         Price: floor(payload.catalog.product.*catalog_item[0].price as Number * 100 default 0),
      },
      Woman: {
         Id: payload.catalog.product.*catalog_item[1].item_number,
         Sizes: sizeOf(payload.catalog.product.*catalog_item[1].*size as Array default []),
         Price: floor(payload.catalog.product.*catalog_item[1].price as Number * 100 default 0),
      }
   }
}

And here is the output:

{
	"Product": {
		"Man": {
			"Id": "QWZ5671",
			"Sizes": 2,
			"Price": 3995
		},
		"Woman": {
			"Sizes": 0,
			"Price": 0
		}
	}
}

This is a fairly similar result to the expected one. The difference lies in lines 9 and 10. We would prefer to get an empty Woman object without properties Sizes and Price.

Emptiness check

The other solution may be a null check. In order to do this, we use conditional elements. Here is the syntax:

Return conditionally content within paranthesis

If the condition is not met, the transformation will omit the whole element. Here is the transformation:

%dw 2.0
output application/json skipNullOn="everywhere"
---
{
   Product: {
      Man: {
         Id: payload.catalog.product.*catalog_item[0].item_number,
         (Sizes: sizeOf(payload.catalog.product.*catalog_item[0].*size as Array default [])) if payload.catalog.product.*catalog_item[0].*size?,
         Price: (floor(payload.catalog.product.*catalog_item[0].price as Number * 100 default 0)) if payload.catalog.product.*catalog_item[0].price?,
      },
      Woman: {
         Id: payload.catalog.product.*catalog_item[1].item_number,
         Sizes: (sizeOf(payload.catalog.product.*catalog_item[1].*size as Array default [])) if payload.catalog.product.*catalog_item[1].*size?,
         Price: (floor(payload.catalog.product.*catalog_item[1].price as Number * 100 default 0)) if payload.catalog.product.*catalog_item[1].price?,
      }
   }
}

In the above data mapping, I have used a question mark to check if an element exists. There are others options like the isEmpty function. I have described it in this article.

Using this mapping we received the expected outcome. However, as you may see we have a lot of repetitions. We have at least 4 additional repetitions that can be omitted. How do we remove them?

Complex match

The last and most readable solution in my opinion is using a match that will evaluate the condition once and shorten the call to properties. Here is the syntax:

Using match for complex matching
Using match for complex matching

Else block is not required. However, when at least one condition (case) could not be met, the transformation will throw an exception if else block will not be present. As you may also notice, the match is against context, and we refer to this context further using $ variable.

Here is the transformation

%dw 2.0
output application/json skipNullOn="everywhere"
---
{
   Product: {
      Man: {
         Id: payload.catalog.product.*catalog_item[0].item_number,
         ( payload.catalog.product.*catalog_item[0] match {
             case is Object -> {
                Sizes: sizeOf($.*size as Array default []),
                Price: floor($.price as Number * 100 default 0)
             }
             else -> {}
         })
      },
      Woman: {
         Id: payload.catalog.product.*catalog_item[1].item_number,
         ( payload.catalog.product.*catalog_item[1] match {
             case is Object -> {
                Sizes: sizeOf($.*size as Array default []),
                Price: floor($.price as Number * 100 default 0)
             }
             else -> {}
         })
      }
   }
}

Mule will check only one condition for Man and Woman section. There is a verification if payload.catalog.product.*catalog_item[0] is either an object or not in line 9 and 19. Furthermore, in lines 10, 11, 20, and 21 we do not repeat the whole path to each property using $ variable.

Summary

We may omit empty elements using conditional elements with the if keyword. On the other hand, we may use a match to check if some conditions have been met. Using match we may omit a lot of the same conditional checks and hence increase readability.

How to avoid repetition in DataWeave for conditional checks?

2 thoughts on “How to avoid repetition in DataWeave for conditional checks?

  1. Thanks for taking the time to write this!
    I just wanted to add that when there’s repetition, you can also extract things into variables or functions.

Leave a Reply

Your email address will not be published.

Scroll to top