Current section
Files
Jump to
Current section
Files
docs/guides/01-getting-started.md
# 01 — Getting Started
<!-- covers: simd_json.package.documentation_layout -->
This guide takes you from installation to your first decoded value, selected
fields, and lazy file stream. You can paste each Elixir example into
`iex -S mix`.
## Install SimdJson
Add `simd_json` to the dependencies in your `mix.exs`:
```elixir
def deps do
[
{:simd_json, "~> 1.0.0"}
]
end
```
Fetch the package and compile your project:
```sh
mix deps.get
mix compile
```
On Ubuntu 24.04 x86-64, Mix downloads a checksum-verified native library. You
do not need Zig, a C++ compiler, or a separate simdjson installation.
Start an IEx session and make sure everything works:
```elixir
iex> SimdJson.decode(~s({"hello":"world"}))
{:ok, %{"hello" => "world"}}
```
If compilation reports an unsupported native target, see
[06 — Deployment and Native Delivery](06-deployment.md).
## Decode a complete value
Imagine that an HTTP response contains an account and its recent activity:
```elixir
json = ~s({
"account": {"id": 7, "name": "Northwind"},
"events": ["signed_in", "exported_report"]
})
{:ok, data} = SimdJson.decode(json)
data["account"]["name"]
# => "Northwind"
```
`decode/1` is the simplest choice when your application needs most or all of
the JSON. It returns ordinary Elixir maps, lists, binaries, numbers, booleans,
and `nil`.
## Return only the fields you need
If you only need the account identifier and name, ask SimdJson for those
fields directly:
```elixir
{:ok, account} =
SimdJson.select(json,
id: ["account", "id"],
name: ["account", "name"]
)
account
# => %{id: 7, name: "Northwind"}
```
Each entry has an output key on the left and a path through the JSON on the
right. Only the selected scalar values are returned to the BEAM.
## Process a large file lazily
For a large file of newline-delimited events, stream rows instead of decoding
the complete file. Create `events.ndjson`:
```json
{"id":1,"kind":"signed_in","internal":{"trace":"a1"}}
{"id":2,"kind":"exported_report","internal":{"trace":"b2"}}
{"id":3,"kind":"signed_out","internal":{"trace":"c3"}}
```
Now project and consume just `id` and `kind`:
```elixir
events =
SimdJson.stream_file("events.ndjson",
format: :ndjson,
fields: [id: ["id"], kind: ["kind"]],
batch_size: 500
)
Enum.each(events, fn event ->
IO.inspect(event, label: "event")
end)
```
The stream requests one bounded batch at a time. The native layer maps and
parses the file, while Elixir receives only the projected rows.
## Handle an error
Non-raising operations return a structured error:
```elixir
case SimdJson.decode(~s({"broken":])) do
{:ok, value} ->
value
{:error, %SimdJson.Error{reason: reason, byte_offset: offset}} ->
IO.puts("JSON failed near byte #{offset}: #{reason}")
end
```
Errors contain stable metadata but never embed the JSON source. Lazy streams
raise `SimdJson.Error` during enumeration because parsing happens as rows are
requested.
## Choose an API
- Use `decode/1` when you need the complete Elixir value.
- Use `select/2` when JSON is already a binary and you need a few scalars.
- Use `select_file/2` to select scalars without first reading the file into a
BEAM binary.
- Use `stream_file/2` for row-oriented work, early stopping, and bounded
parser and result-batch memory.
Next: [02 — Decoding JSON](02-decoding-json.md).