Skip to main content
Documentation

JSON Values

JsonValue is Nexigon’s free-form JSON value type. It can be any JSON value: object, array, string, number, boolean, or null. Device properties, command inputs and outputs, metadata, and other API fields use it when they need arbitrary JSON.

The public json Sidex schema defines JsonValue together with generic tools for evaluating, selecting, comparing, and changing JSON values:

  • JsonValue: any JSON value.
  • JsonExpr: an expression that evaluates to a JSON value.
  • JsonPath: a path into another JSON value.
  • JsonMutation: a declarative transformation applied to a JSON value.

Expressions

Every JSON expression evaluates to a JSON value. Predicate expressions are ordinary expressions whose final value must be JSON true or false.

Evaluation receives a caller-provided context of named JSON values. For instance, in operations, device properties are exposed as named values, so this expression reads the current value of the device property dev.nexigon.ota.current:

{
  "kind": "NamedValue",
  "name": "dev.nexigon.ota.current"
}

Named value identifiers must be 1 to 256 characters, start with a letter or digit, and contain only letters, digits, dots, underscores, or hyphens.

Expression Format

Expression objects are tagged with kind:

{ "kind": "Literal", "value": true }

Variants whose payload is a record inline that record’s fields. Variants whose payload is a raw JSON value or another expression use a payload-specific field such as value or expr.

Defaults And Missing Values

Missing named values and missing paths are evaluation errors by default. They are not treated as false or null.

NamedValue and Get can include default to recover from missing values:

{
  "kind": "NamedValue",
  "name": "com.example.optional",
  "default": { "kind": "Literal", "value": null }
}

For Get, the default expression applies when the input expression is missing or when the path is missing. Other errors, such as traversing a scalar value as if it were an object, still fail evaluation. Default expressions are evaluated only when needed; if a default expression itself fails, that error is reported.

Expression Kinds

KindFieldsResult
LiteralvalueReturns the constant JSON value in value.
CurrentnoneReturns the value currently being mutated. Valid only inside a mutation expression.
NamedValuename, optional defaultReads a named JSON value from the evaluation context.
Getexpr, path, optional defaultGets a nested value from the result of another expression.
Existsexpr, pathReturns whether a path resolves against the result of expr.
Equalsleft, rightReturns whether two JSON values are equal.
NotEqualsleft, rightReturns whether two JSON values are not equal.
LessThanleft, rightOrdered comparison over JSON numbers or strings.
LessThanOrEqualsleft, rightOrdered comparison over JSON numbers or strings.
GreaterThanleft, rightOrdered comparison over JSON numbers or strings.
GreaterThanOrEqualsleft, rightOrdered comparison over JSON numbers or strings.
Inleft, rightReturns whether left appears in the JSON array produced by right.
AndexprsShort-circuiting conjunction over one or more expressions that must evaluate to booleans.
OrexprsShort-circuiting disjunction over one or more expressions that must evaluate to booleans.
NotexprBoolean negation over an expression that must evaluate to a boolean.
VersionRequirementexpr, requirementEvaluates expr as a version string and matches it against a version requirement.

Boolean operators short-circuit. And stops at the first false result, and Or stops at the first true result. Operation definitions require at least one operand.

Exists is the explicit way to ask whether a path is present:

{
  "kind": "Exists",
  "expr": {
    "kind": "NamedValue",
    "name": "dev.nexigon.ota.current"
  },
  "path": ["version"]
}

If the input expression itself is missing, Exists evaluates to false. A non-empty path against a scalar value also evaluates to false; the empty path evaluates to true for any resolved JSON value.

Predicate Examples

Match a device property exactly:

{
  "kind": "Equals",
  "left": {
    "kind": "NamedValue",
    "name": "com.example.rollout.ring"
  },
  "right": { "kind": "Literal", "value": "stable" }
}

Match a nested field in a JSON property:

{
  "kind": "Equals",
  "left": {
    "kind": "Get",
    "expr": {
      "kind": "NamedValue",
      "name": "dev.nexigon.ota.status"
    },
    "path": ["state"]
  },
  "right": { "kind": "Literal", "value": "completed" }
}

Match when a version is absent or different:

{
  "kind": "Or",
  "exprs": [
    {
      "kind": "Not",
      "expr": {
        "kind": "Exists",
        "expr": {
          "kind": "NamedValue",
          "name": "dev.nexigon.ota.current"
        },
        "path": ["version"]
      }
    },
    {
      "kind": "NotEquals",
      "left": {
        "kind": "Get",
        "expr": {
          "kind": "NamedValue",
          "name": "dev.nexigon.ota.current"
        },
        "path": ["version"]
      },
      "right": { "kind": "Literal", "value": "2.0.0" }
    }
  ]
}

Match a semantic version range:

{
  "kind": "VersionRequirement",
  "expr": {
    "kind": "NamedValue",
    "name": "dev.nexigon.agent.version"
  },
  "requirement": ">=1.8.0, <2.0.0"
}

Paths

Paths describe how to locate a value inside another JSON value. JsonPath is a JSON array of path segments.

String segments address object members. Non-negative integer segments address array elements and are represented as u32 in the Sidex schema. The empty path [] addresses the current value itself:

["firmware", "version"]
["items", 0, "id"]

A numeric string and a numeric segment are different: "0" addresses an object member named 0, while 0 addresses the first array element. Object member keys may be at most 256 characters.

Get Example

Use Get to read nested values:

{
  "kind": "Get",
  "expr": {
    "kind": "NamedValue",
    "name": "dev.nexigon.ota.status"
  },
  "path": ["state"]
}

Use default when a missing input expression or missing path should produce a fallback value:

{
  "kind": "Get",
  "expr": {
    "kind": "NamedValue",
    "name": "dev.nexigon.ota.status"
  },
  "path": ["progress"],
  "default": { "kind": "Literal", "value": 0 }
}

Mutations

JSON mutations describe how to transform a current JSON value. Like expressions, mutations are evaluated in a caller-provided context.

Mutation Format

Mutation objects are tagged with mutation:

{
  "mutation": "Set",
  "path": [],
  "expr": { "kind": "Literal", "value": true }
}

Mutation Kinds

MutationFieldsEffect
Setpath, exprReplaces the value at path with the expression result. An empty path replaces the root value.
MergePatchpath, exprEvaluates expr and applies it as an RFC 7396 JSON Merge Patch to the value at path. An empty path patches the root value.
RemovepathRemoves the value at path. Missing values are ignored.

Path Mutation Behavior

Path-targeted Set and MergePatch can create missing object parents. Array indexes must already exist. Remove ignores missing values, but it still fails when the path tries to traverse through a value of the wrong type.

An empty path targets the root for Set and MergePatch. Remove requires a non-empty path; use the operation’s RemoveDeviceProperty step to delete an entire property.

Examples

Replace a whole value:

{
  "mutation": "Set",
  "path": [],
  "expr": {
    "kind": "Literal",
    "value": { "version": "2.0.0" }
  }
}

Apply a merge patch:

{
  "mutation": "MergePatch",
  "path": [],
  "expr": {
    "kind": "Literal",
    "value": {
      "version": "2.1.0",
      "metadata": { "ring": "stable" }
    }
  }
}

Set one value at a path:

{
  "mutation": "Set",
  "path": ["features", "ota"],
  "expr": { "kind": "Literal", "value": true }
}

Apply a merge patch to one value at a path:

{
  "mutation": "MergePatch",
  "path": ["metadata"],
  "expr": { "kind": "Literal", "value": { "ring": "stable" } }
}

Remove one value at a path:

{
  "mutation": "Remove",
  "path": ["features", "legacy"]
}

If a mutation cannot be applied, for example because a path traverses a scalar value as if it were an object, evaluation fails and the caller reports the error according to the API using the mutation.

Evaluation Errors

The evaluator classifies each error with:

FieldMeaning
kindError classification.
messageHuman-readable explanation.
pathLogical evaluation location, such as a named value, JSON path, or expression operator.

These diagnostics are implementation details rather than public API payloads. Operation APIs retain or return concise formatted error messages at the relevant step, gate, or selector boundary.

Error kinds are:

KindMeaning
MissingNamedValueA named value was not present in the evaluation context.
MissingPathA JSON path did not resolve.
InvalidTypeAn expression received a value of the wrong JSON type.
InvalidOperationThe expression or path is invalid.
InvalidVersionA value was not a valid version.
InvalidVersionRequirementA version requirement could not be parsed.

Sidex Schema

The public API types are defined in the json Sidex schema:

schema json

JSON types.

opaque

JsonValue

JSON value.

JSON shape: any