← All ivy-nodes
IVYXSTUDIO · IVY NODE
J

JSON Extract

ivy.node.json-extract · v0.1.0

ivyx✓

Reads one value out of a JSON document by dotted path and reports whether it was found. Segments split on a dot, an integer segment indexes a list, and a path that does not exist yields the supplied default with found set to false rather than raising — so the same node answers both 'give me this field' and 'does this field exist'. Pure compute: no network, no file access.

#json#extract#path

Inputs

FieldTypeDescription
sourcerequiredNot declaredThe JSON document: text to parse, or an already-decoded object or array. Deliberately UNTYPED: the gateway's validator compares a declared type array by stringifying it, so `["string","object","array"]` refuses every value with `expected type "string,object,array"` — measured 2026-09-30. An undeclared type accepts all three, and the cell checks the shape itself.
pathrequiredstringDotted path, e.g. `choices.0.message.content`.
defaultNot declaredReturned when the path is absent. Null when omitted.

Outputs

FieldTypeDescription
valuerequiredNot declaredThe value at the path, or the default.
foundrequiredbooleanFalse when the path was absent and the default was used.

Source

python

# Input preparation
inp = __ivy_ctx__["nodes"][__ivy_node_id__]["input"]
source = inp["source"]
path = inp["path"]
default = inp.get("default")

# Compute
import json as _json
from typing import Any

_MISSING = object()

def _parse(source: Any) -> Any:
    """A JSON string is parsed; anything already decoded is used as it is."""
    if isinstance(source, (dict, list)):
        return source
    if isinstance(source, str):
        try:
            return _json.loads(source)
        except _json.JSONDecodeError as exc:
            raise ValueError(f"source is not valid JSON: {exc.msg}") from None
    raise ValueError("source must be a JSON string, object or array")

def _step(node: Any, key: str) -> Any:
    """One segment. A list takes an integer index, a dict takes a key."""
    if isinstance(node, list):
        try:
            index = int(key)
        except ValueError:
            return _MISSING
        return node[index] if -len(node) <= index < len(node) else _MISSING
    if isinstance(node, dict):
        return node.get(key, _MISSING)
    return _MISSING

def extract(source: Any, path: str, default: Any = None) -> Any:
    """
    Read one value out of a JSON document by dotted path.

    Segments are split on `.`; a segment that is an integer indexes a list,
    so `items.0.name` and `items[0].name` describe the same place — the
    brackets are stripped rather than parsed, because a second syntax for
    one idea is a second thing to get wrong.

    A path that does not exist returns `default` instead of raising: a
    missing field is the ordinary case when reading somebody else's JSON,
    and a node that raises on it cannot be used to check whether a field is
    there.
    """
    if not isinstance(path, str) or not path.strip():
        raise ValueError("path is required")
    document = _parse(source)
    node: Any = document
    for segment in path.replace("[", ".").replace("]", "").split("."):
        if segment == "":
            continue
        node = _step(node, segment)
        if node is _MISSING:
            return default, False
    return node, True

value, found = extract(source, path, default)

# Output collection (runner reads __ivy_ctx__)
out = __ivy_ctx__["nodes"][__ivy_node_id__]["output"]
out["value"] = value
out["found"] = found

Tests

Requires: python:3.9

  • nested-list

    A path through a list index returns the exact string at that place.

  • bracket-syntax

    Bracket indexing describes the same place as a dotted integer.

  • absent-path

    A path that is not there yields the default and found false, without raising.

  • invalid-json

    Text that is not JSON is an error, not a silent default.