02 — Decoding JSON

View Source

Use decode/1 when you want to turn one complete JSON value into ordinary Elixir data. In this tutorial, you will decode an order, work with the result, and handle malformed input.

Decode your first value

Start with a small order:

json = ~s({
  "order_id": 4815,
  "paid": true,
  "items": [
    {"sku": "BOOK-1", "quantity": 2},
    {"sku": "PEN-4", "quantity": 5}
  ]
})

{:ok, order} = SimdJson.decode(json)

The result is made from familiar Elixir values:

order["order_id"]
# => 4815

Enum.map(order["items"], & &1["sku"])
# => ["BOOK-1", "PEN-4"]

JSON object keys remain binaries. SimdJson never creates atoms from input, so you can safely decode documents with keys you have not seen before.

Choose tagged or raising results

decode/1 is convenient at trust boundaries because success and failure are explicit:

case SimdJson.decode(payload) do
  {:ok, value} ->
    {:accepted, value}

  {:error, %SimdJson.Error{} = error} ->
    {:rejected, error.reason}
end

When invalid JSON is exceptional, use decode!/1:

order = SimdJson.decode!(json)

It returns the value directly and raises SimdJson.Error on failure. Both forms use the same parser and produce the same Elixir data.

Understand the result types

SimdJson maps JSON values as follows:

JSON valueElixir value
objectmap with binary keys
arraylist
stringbinary
integerinteger
fraction or exponentfloat
true or falseboolean
nullnil

If an object repeats a key, the last value wins. Integers remain exact when they fit the supported native range; values outside that range return an error instead of being silently rounded.

Handle malformed input

Errors include a stable reason and, when available, a byte offset:

invalid = ~s({"order_id":4815,"items":[})

case SimdJson.decode(invalid) do
  {:error, %SimdJson.Error{reason: reason, byte_offset: offset}} ->
    IO.inspect({reason, offset}, label: "decode failed")

  {:ok, _order} ->
    :unexpected_success
end

Error inspection is redacted: it will not print the JSON source, native addresses, or internal request data.

Know when not to decode

Decoding builds the complete Elixir tree, so the result naturally grows with the document. That is the right tradeoff when your application needs the whole value.

If the order contains thousands of fields but you only need its identifier, select that field instead. If the source is a large row sequence, stream it.

decode/2 currently accepts only an empty option list, and input must be a binary rather than iodata:

SimdJson.decode(json, [])
# => {:ok, order}

Previous: 01 — Getting Started
Next: 03 — Selecting Fields