# structKeyExists vs isNull vs ParameterExists vs isDefined vs elvis operator



# structKeyExists vs isNull vs ParameterExists vs isDefined vs elvis operator

CFML has several ways to ask "is there a value here?". They give the same answer for an existing value, but they differ for a key or variable that holds `null`, and the answer can change with the [Null Handling in CFML](null-support.md) setting.

All results on this page were verified on Lucee 7.1.2.19 and 8.0.0.211 (both versions behave the same).

## Test cases

```cfml
s = {};
s.k = nullValue();                              // key holding null
json = deserializeJSON( '{"k":null,"x":1}' );   // JSON null, same results as s.k
empty = { k: "" };                              // key with an empty string
variables.v = nullValue();                      // variable set to null
a = { x: 1 };                                   // a.b.c with b missing
```

Struct literals (`{ k: nullValue() }`, `[ k: nullValue() ]`) and assignments (`s.k = nullValue()`) behave the same.

## Partial null support (default)

| Case | `structKeyExists()` | `isNull()` | `parameterExists()` | `isDefined()` | `x ?: "default"` | `x?.y` | `x` |
|---|---|---|---|---|---|---|---|
| struct key holding null | false | true | false | false | `"default"` | null | error |
| missing struct key | false | true | false | false | `"default"` | null | error |
| struct key with `""` | true | false | true | true | `""` | `""` | `""` |
| undefined variable | false | true | false | false | `"default"` | – | error |
| `variables` / `local` variable set to null | false | true | false | false | `"default"` | – | error |
| nested `a.b.c`, `b` missing | false (`a`, `"b"`) | true | false | false | `"default"` | null | error |

With partial null support, a key holding null behaves like a missing key for every check, and reading it directly throws. The key is still stored in the struct, though: `structCount()` and `structKeyList()` include it, and `serializeJSON()` writes it as `null`.

```cfml
s = deserializeJSON( '{"a":null,"b":1}' );
dump( structKeyExists( s, "a" ) );  // false
dump( isNull( s.a ) );              // true
dump( structCount( s ) );           // 2
dump( serializeJSON( s ) );         // {"a":null,"b":1}
```

## Full null support

| Case | `structKeyExists()` | `isNull()` | `parameterExists()` | `isDefined()` | `x ?: "default"` | `x?.y` | `x` |
|---|---|---|---|---|---|---|---|
| struct key holding null | **true** | true | false | **true** | `"default"` | null | null |
| missing struct key | false | true | false | false | `"default"` | null | error |
| struct key with `""` | true | false | true | true | `""` | `""` | `""` |
| undefined variable | false | true | false | false | `"default"` | – | error |
| `variables` / `local` variable set to null | **true** | true | false | **true** | `"default"` | – | null |
| nested `a.b.c`, `b` missing | false (`a`, `"b"`) | true | false | false | `"default"` | null | error |

With full null support, a key or variable holding null **exists**: `structKeyExists()` and `isDefined()` return true, and reading it returns null instead of throwing. `isNull()` is true either way.

Two things don't change with full null support:

- The elvis operator returns the default for null, so `s.k ?: "default"` is `"default"` whether `k` is missing or holds null.
- `parameterExists()` returns false for a null value.

For the `isDefined()` column above the name was passed as a literal string (`isDefined( "s.k" )`). With full null support, a name held in a variable (`name = "s.k"; isDefined( name )`) returned false for a null value in the same test, like `parameterExists()`.

## Elvis operator and safe navigation

The elvis operator `?:` returns the right-hand side when the left-hand side is null or doesn't exist, without throwing. Safe navigation `?.` stops at the first missing part of a path and returns null:

```cfml
a = { x: 1 };
dump( a.b.c ?: "default" );  // "default"
dump( isNull( a?.b?.c ) );   // true
dump( a.b.c );               // error: key [B] doesn't exist
```

An empty string is a value, so `empty.k ?: "default"` returns `""`.

## Recommendations

- To get a value or a fallback, use the elvis operator: `name = form.name ?: "anonymous";`. It behaves the same in both null support modes.
- To read a nested value that may be missing, use safe navigation: `city = user?.address?.city;`.
- To check for "no value", use [IsNull()](../reference/functions/isnull.md): it's true for null, missing keys and undefined variables in both modes. Use a scoped reference (`isNull( local.result )`), see [Null Handling in CFML](null-support.md).
- To check whether a key exists, use [StructKeyExists()](../reference/functions/structkeyexists.md), and keep in mind that a key holding null only counts as existing with full null support.
- To tell "key holds null" apart from "key is missing", you need full null support (`structKeyExists()` true plus `isNull()` true).
- Avoid [IsDefined()](../reference/functions/isdefined.md) for null checks: its result depends on the null support mode and, with full null support, on whether the name is a literal string.
- [ParameterExists()](../reference/functions/parameterexists.md) is a legacy function that calls `isDefined()` internally. Use `structKeyExists()`, `isNull()` or the elvis operator instead.

# Categories

[Core CFML Language](../categories/core.md), [Decision logic](../categories/decision.md)

# See Also

[IsDefined()](../reference/functions/isdefined.md), [IsNull()](../reference/functions/isnull.md), [NullValue()](../reference/functions/nullvalue.md), [ParameterExists()](../reference/functions/parameterexists.md), [StructKeyExists()](../reference/functions/structkeyexists.md), [Null Values in Structs and Arrays](null-structs-and-arrays.md), [Null Handling in CFML](null-support.md), [Operators](../guides/developing-with-lucee-server/operators.md)