# JSON Documents

This page explains how ToriiDB stores JSON values and how to read nested fields with dot-notation paths.

## Stored Representation

A value is `json` when it is valid JSON and its outer value is an object or array. The entry keeps the raw string, and a parsed copy is cached in the unexported `parsed` field.

| Method | Role | Lock |
|---|---|---|
| `Value()` | Raw serialized string | any |
| `parseAndCache()` | Decodes on cache miss and stores the result | write lock, or single-threaded replay |
| `cached()` | Returns an already warmed cache without mutating | read lock is enough |
| `setValue(v)` | Replaces the raw string and clears the cache | write lock |
| `setParsed(obj)` | Marshals `obj` and stores both forms together | write lock |

Mixing `parseAndCache()` into read-locked paths would let two readers, such as two `Query` calls, race on the cache. Every JSON write therefore warms the cache under the write lock, and readers only use `cached()`.

## Dot-Notation Paths

`Exec` splits a key at its first `.`; the remainder is split on `.` with empty parts dropped:

```text
user.profile.level   → key "user", path [profile level]
user.tags.0          → key "user", path [tags 0]
user.                → key "user", no path
```

Objects are addressed by field name and arrays by decimal index. In the REPL, a top-level key containing `.` can therefore not be addressed by dot notation; the Go API and the daemon take the key and the path separately.

## Reading Fields

```text
GET user.profile.level
EXIST user.profile.level
TYPE user.profile.level
```

`GetField` walks objects and arrays (including a top-level array) under the read lock. Values are rendered with `utils.Vtoa`: whole numbers without a decimal point, objects and arrays as compact JSON, and `null` as `(nil)`. `TypeField` runs normal type detection on that rendered text.

## Related Pages

- [Writing JSON Fields](/json-documents-writes)
- [QUERY Expressions](/querying-query)
- [Value Types](/core-concepts-value-types)
