The Magic of Null-Checking

Null checking is somewhat special compared to other programming languages. In Appian we deal with statically typed rule inputs and dynamically typed local variables. In many situations, we need to check if these variables are Null. For this exercise, I will expand the meaning of a simple Null-check and try to define what is a meaningful value for a variable.

Let’s have a look at some examples.

The Challenge

Variables of a simple type like Text, Integer, or Date can easily checked for Null. Use the Appian function a!isNullOrEmpty() to do so. It will return true in case a variable is one of:

  • Null
  • an empty Text
  • an empty list

There is also an a!isNotNullOrEmpty() which implements the reverse logic. While this will cover the basics, I will cover all the edge cases now.

A Text variable can be Null, or contain zero or more characters. While this is covered by a!isNullOrEmpty(), what about a value like ” ” or ” “? Do you want to consider this to be a valid value?

In Appian, we can also work with list type variables. A list can be Null, or contain zero or more items. Again, this is covered by a!isNullOrEmpty(). Now think of a list that contains a number of Null values? Valid or not?

Then, we have data structures like dictionaries, maps, CDTs and Record Types. And lists of them, with similar issues. Is a map without any fields a valid value? What about a non-Null CDT variable with all fields Null?

Combine this with the older Appian functions that fail when passed a null value, and you have an ugly problem that is a real challenge, especially for beginners.

The Solution

To cover all these edge cases, we need an Expression Rule that tells us whether a variable contains a non-Null and meaningful value. The following code snippet defines a Null value like this:

  • Null
  • Empty Text
  • Text with only non-printable characters
  • Lists containing only Null values
  • Lists of the Any type containing only Null values
  • Maps without any fields
  • Maps with all fields Null
  • DataSubsets with an empty data field
  • Maps returned by a!queryRecordType() with an empty data field
    local!typeNum: a!refreshVariable(
      value: typeof(ri!value),
      refreshAlways: true
    /* Strings */
      local!typeNum = 'type!{}Text',
      /* Ignore white space */
      len(trim(ri!value)) = 0,
    /* List of Variant */
      local!typeNum = 'type!{}Variant?list',
      all(a!isNullOrEmpty(_), a!flatten(ri!value)),
    /* Lists */
      /* Try to take first item in list and cast it to a list of itself */
      /* then compare its datatype to the incoming data type */
      local!typeNum = runtimetypeof(cast(runtimetypeof({index(ri!value, 1, null)}), ri!value)),
      /* Flatten takes care of lists in lists in lists in ... */
      /* List has zero non-null items, length() already ignores null values*/
      length(a!flatten(ri!value)) = 0,

    /* Map */
      local!typeNum = 'type!{}Map',
      /* Has no keys at all or all values are null */
        length(a!keys(ri!value)) = 0,
        all(a!isNullOrEmpty(_), index(ri!value, a!keys(ri!value), null))
    /* DataSubsets or Maps returned by queryRecordType */
        local!typeNum = 'type!{}DataSubset',
          local!typeNum = 'type!{}Map',
          contains(a!keys(ri!value), "data"),

      /* No idea what this could be so it is considered to not be null */

This Expression Rule has evolved over the last 6 years thanks to many colleagues and friends. I use it in Expressions in all my applications to build a robust foundation.

I hope it helps you find an answer to the question “Is there something in there?”

3 thoughts on “The Magic of Null-Checking

  1. Maybe I’m the only person confused, but what would be the type of ri!value in this expression rule? Any Type? Or is it not meant to be universal?

Leave a Reply