Skip to content

Handling Instance and Type Parameters in Revit with JSONata

This document explains how to correctly retrieve shared parameters in Revit models when writing JSONata rules. In Revit, parameters may exist either on the FamilyInstance (Instance) or on the FamilySymbol (Type). A robust validation rule must therefore handle both cases.


JSONata Filter — Instance / Type Parameter Resolution

The following filter demonstrates a reusable pattern for retrieving parameters regardless of where they are stored.

(
  /* =========================================================
     1. Parameter GUID configuration
     ---------------------------------------------------------
     Logical parameter names mapped to their GUIDs.
     Using GUIDs avoids ambiguity because parameter names
     in Revit are not guaranteed to be unique.
  ========================================================= */

  $paramGuids := {
    "Objecttype_Number": "104c3657-6918-4b2b-a0b9-bf4b05c8621a",
    "Objecttype_Description": "2bc41923-4e29-4bf8-9911-992c87f16d26"
  };


  /* =========================================================
     2. Parameter metadata cache
     ---------------------------------------------------------
     Build a lookup table of parameter metadata once.
     This avoids repeatedly searching for parameter definitions
     and improves performance on large models.
  ========================================================= */

  $paramMetaByGuid :=
    $merge(
      $[type = "Parameter" and values.guid in $paramGuids.*].{
        $string(values.guid): {
          "guid": values.guid,
          "name": values.name
        }
      }
    );


  /* =========================================================
     3. Shared parameter retrieval helper
     ---------------------------------------------------------
     Retrieves a shared parameter from a given object.

     The object can be:
     - a FamilyInstance
     - a FamilySymbol

     Parameters in DAQS JSON are stored using the format:
     values.p_<GUID>
  ========================================================= */

  $getSharedParam := function($object, $logicalName){
    (
      $guid := $lookup($paramGuids, $logicalName);

      $meta :=
        $guid
        ? $lookup($paramMetaByGuid, $string($guid))
        : undefined;

      $sp :=
        $guid and $exists($object.values)
        ? $lookup($object.values, "p_" & $guid)
        : undefined;

      $present := $exists($sp);

      {
        "paramExist": $present,
        "value": $present and $exists($sp.value) ? $sp.value : null,
        "valueAsString": $present and $exists($sp.valueAsString) ? $sp.valueAsString : null,
        "guid": $meta ? $meta.guid : $guid,
        "name": $meta ? $meta.name : $logicalName
      }
    )
  };


  /* =========================================================
     4. Effective parameter helper
     ---------------------------------------------------------
     In Revit, parameters may be defined either on:

     • FamilyInstance (Instance parameter)
     • FamilySymbol (Type parameter)

     In this project a parameter exists in only one location.
     This helper retrieves the effective value regardless
     of where the parameter is stored.
  ========================================================= */

  $getEffectiveParam := function($instance, $symbol, $logicalName){
    (
      $inst := $getSharedParam($instance, $logicalName);

      $inst.paramExist
        ? $inst
        : $getSharedParam($symbol, $logicalName)
    )
  };


  /* =========================================================
     5. Symbol scope
     ---------------------------------------------------------
     Select relevant FamilySymbols.

     Limiting scope improves performance when processing
     large models.
  ========================================================= */

  $symbols :=
    $[
      type = "FamilySymbol"
      and values.category.type = "Model"
    ];


  /* =========================================================
     6. Build symbol lookup index
     ---------------------------------------------------------
     Create a fast lookup dictionary:

     symbolId → symbol object

     This allows instances to resolve their type quickly.
  ========================================================= */

  $symIndex :=
    $merge(
      $symbols.{
        $string(id): $
      }
    );


  /* =========================================================
     7. Main query — iterate over FamilyInstances
  ========================================================= */

  $[
    type = "FamilyInstance"
  ].(

    $sym := $lookup($symIndex, $string(parent.id));

    $sym ?

      (
        /* Retrieve parameters regardless of storage location */

        $Objecttype_Number :=
          $getEffectiveParam($, $sym, "Objecttype_Number");

        $Objecttype_Description :=
          $getEffectiveParam($, $sym, "Objecttype_Description");

        {
          "id": id,
          "type": type,
          "name": name,

          "Category": $sym.values.category.label,
          "assemblyCode": $sym.values.assemblyCode,

          "Objecttype_Number": $Objecttype_Number,
          "Objecttype_Description": $Objecttype_Description
        }
      )

    : ()
  )
)

The Problem

In Revit, parameters may be defined at two different levels:

Level Revit Object
Instance FamilyInstance
Type FamilySymbol

A parameter defined on the Type automatically applies to all instances of that type.

However, many validation scripts only inspect FamilyInstances. When a parameter exists only on the FamilySymbol, the rule may incorrectly report that the parameter is missing.


Example Scenario

Element Type Parameter Location
Light 1 LightTypeA Type
Light 2 LightTypeA Type

If the validation checks only the instance:

values.p_GUID

the parameter appears missing even though it exists on the type.


The Effective Parameter Pattern

To correctly validate Revit data, rules should check:

Instance parameter
OR
Type parameter

The filter implements this using a helper function:

$getEffectiveParam($instance, $symbol, $logicalName)

Logic:

If instance parameter exists
    use instance value
Else
    use type value

This ensures the rule works regardless of where the parameter is stored.


Why GUIDs Are Used

Shared parameters are retrieved using GUIDs rather than names.

Reasons:

  • Parameter names are not guaranteed to be unique
  • Multiple parameters can share the same name
  • GUIDs uniquely identify the parameter definition

Example configuration:

$paramGuids := {
  "Objecttype_Number": "104c3657-6918-4b2b-a0b9-bf4b05c8621a",
  "Objecttype_Description": "2bc41923-4e29-4bf8-9911-992c87f16d26"
};

Performance Considerations

Two optimizations are used.

Parameter Metadata Cache

Parameter definitions are retrieved once and stored in a lookup table:

GUID → Parameter metadata

This avoids repeated searches through all parameters.

Symbol Lookup Index

A lookup index is created for symbols:

symbolId → symbol

This allows instances to quickly resolve their type.


Result Structure

Each FamilyInstance in the output contains:

  • instance id
  • element name
  • category
  • assembly code
  • resolved parameter values

Conceptually:

FamilyInstance
      │
      ▼
Resolve FamilySymbol
      │
      ▼
Retrieve effective parameter

Key Takeaway

Reliable Revit validation must handle both parameter locations:

Instance parameter
OR
Type parameter

This Instance/Type resolution pattern is a fundamental building block for most BIM validation rules.