Null check for a!map in a process

Certified Senior Developer

Hi together,

I have a "map" value in a process.

I realized that an IsNull/emptycheck rule I used in a gateway down the line, was failing of evaluating that there was (in my understanding) an empty value

cast(
  'type!{http://www.appian.com/ae/types/2009}Boolean',
  length(a!flatten(ri!value)) = 0
)



Even the new rules a!isNullOrEmpty ..... are not recognizing this properly.

Does anybody experienced this as well? (not difficult to solve, but it was pretty unexpected)

Kind regards,

Richard

  Discussion posts and replies are publicly visible

  • Hi Richard,

    You should check what the value of the map type is inside the process instance. If it's null then it's length will be 0 but if the map value has been initialize during the process and has a structure, then it's value will not be 0. Something like this: 

    What was the error message that you were getting in the process instance?

  • 0
    Certified Senior Developer
    in reply to Dimitris Soulovaris

    yeah the error message is fine. Nothing complicated.
    I think the {} is my issue here.




    A cdt structure is differently represented in a process then.

    I just expected a different behavior as the value is shown as {} not as (id: null, name: null) 
    it is more about the visual representation where i am struggling, i think.
    The issue of null values in a structure vs empty list -> {} is an empty list to me.

    Perhaps its just me. Smiley

    Edit: a!map doesn't seems to be intialized as a "list of variant" is saved into it.
    Edit2: Which is not working in an expression editor but in a process, if a process variable is of type a!map but the rule result you like to store has the type "list of variant".
    ...yeeeah....fun.....

  • All the data structures in Appian, if have been initialized in an expression or interface then are not considered as Null or Empty.  Therefore its length will be always at least 1. This is a key point. 

    In your example, you are comparing different things.. A single CDT with a list of CDTs.

    You have flagged the rule input as an array. If you uncheck that flag then the result will be different. 

      

  • 0
    Certified Lead Developer

    I would implement a null check for a map like this:

    or(
      length(a!keys(ri!map)) = 0,
      all(a!isNullOrEmpty(_), index(ri!map, a!keys(ri!map), null))
    )

  • 0
    Certified Senior Developer
    in reply to Dimitris Soulovaris

    but it is not properly initialized. Thats the thing. 
    i was more hinting the following situation:

    a!map(id: null,name null)  which is comparable to type!....(null)
    and an empty list {} of a certain cdt type and a!map with the value of {} in a process.

    {} -> to me always an empty list of a certain type 

    Edit: -> i got it. sorry my misread.
    {} in array context vs {} in single item context. Yeah overseen aspect by me. Thank you for the hint.

  • It's important to understand what type of values you have at the time so you know what to compare and what to expect from that comparison.

    So, in your first question, you were looking for a way to check whether all fields of the map are null or empty because you thought the map was null. 

  • 0
    Certified Lead Developer
    in reply to Stefan Helzle

    At that point, one should just have a designated helper rule designed to brute-force check for the emptiness of an array (which i always have had, anyway).  It's disappointing that a!isNullOrEmpty() seemingly fails for certain corner cases, though I'd've thought it would work for an empty a!map() array (i haven't checked TBH).

  • 0
    Certified Lead Developer
    in reply to Mike Schmitt

    True. Our null checker expression turned into black magic ;-)

    if(
      a!isNullOrEmpty(ri!value),
      true,
      a!localVariables(
        local!typeNum: a!refreshVariable(
          value: runtimetypeof(ri!value),
          refreshAlways: true
        ),
        
        /* Strings */
        if(
          local!typeNum = 'type!{http://www.appian.com/ae/types/2009}Text',
          /* Ignore white space */
          len(trim(ri!value)) = 0,
          
        /* List of Variant */
        if(
          local!typeNum = 'type!{http://www.appian.com/ae/types/2009}Variant?list',
          all(a!isNullOrEmpty(_), a!flatten(ri!value)),
          
        /* Lists */
        if(
          /* 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 */
        if(
          local!typeNum = 'type!{http://www.appian.com/ae/types/2009}Map',
          /* Has no keys at all or all values are null */
          or(
            length(a!keys(ri!value)) = 0,
            all(a!isNullOrEmpty(_), index(ri!value, a!keys(ri!value), null))
          ),
          
        /* DataSubsets or Maps returned by queryRecordType */
        if(
          or(
            local!typeNum = 'type!{http://www.appian.com/ae/types/2009}DataSubset',
            and(
              local!typeNum = 'type!{http://www.appian.com/ae/types/2009}Map',
              contains(a!keys(ri!value), "data"),
            )
          ),
          a!isNullOrEmpty(ri!value.data),
    
          /* No idea what this could be so it is considered to not be null */
          false
        )))))
      )
    )

  • 0
    Certified Lead Developer
    in reply to Stefan Helzle

    Appian, please don't make us have to write things like this ^.