03 — Selecting Fields
View SourceOften you do not need to turn a complete JSON document into an Elixir tree. This tutorial shows how to extract a small set of scalar values in one native traversal.
Build a projection
Suppose an API returns a customer and their orders:
json = ~s({
"customer": {"id": 1234, "name": "Acme", "plan": "business"},
"orders": [
{"sku": "ABC-123", "total": 29.95},
{"sku": "XYZ-900", "total": 8.50}
]
})You only need the customer identifier, name, and first SKU. Describe each result field with an output key and a JSON path:
fields = [
{:customer_id, ["customer", "id"]},
{:customer_name, ["customer", "name"]},
{:first_sku, ["orders", 0, "sku"]}
]
{:ok, result} = SimdJson.select(json, fields)
result
# => %{customer_id: 1234, customer_name: "Acme", first_sku: "ABC-123"}A binary path segment enters an object. A non-negative integer enters an array at that index. The output keys are exactly the atoms or binaries you supplied; JSON keys are never converted to atoms.
Select scalar values
Selected leaves can be strings, integers, floats, booleans, or null:
json = ~s({"name":"Ada","score":9.5,"active":true,"note":null})
SimdJson.select(json,
name: ["name"],
score: ["score"],
active: ["active"],
note: ["note"]
)
# => {:ok, %{name: "Ada", score: 9.5, active: true, note: nil}}Objects and arrays are not scalar results. Selecting one returns an
:incorrect_type error instead of materializing that container. A failure
returns no partial map.
Every selected string is copied into a fresh binary. Keeping a small result therefore does not retain the complete source through a substring.
Select directly from a file
When the source is already on disk, avoid File.read/1 and pass the path to
select_file/2:
{:ok, customer} =
SimdJson.select_file("customer.json",
id: ["customer", "id"],
plan: ["customer", "plan"]
)
customer
# => %{id: 1234, plan: "business"}The BEAM passes only the path across the native boundary. The file must remain unchanged until the operation finishes. This avoids a complete BEAM binary copy, although the one-shot simdjson structural index can still grow with the document.
Open a document for explicit cleanup
Most callers can use select/2 or select_file/2 directly. If you need an
explicit native lifetime, open and close a document yourself:
with {:ok, document} <- SimdJson.open(json),
{:ok, result} <- SimdJson.select(document, id: ["customer", "id"]) do
:ok = SimdJson.close(document)
{:ok, result}
endThe process that opens a document owns it. Selection is forward-only and
one-shot once native cursor access begins, whether it succeeds or fails. Open
a new document for another projection. Calling close/1 again from the owner
is safe; another process receives :not_owner.
SimdJson validates the complete JSON source even when all selected values were found early. If an object repeats a requested key, its first occurrence is the selected value.
Previous: 02 — Decoding JSON
Next: 04 — Streaming Large Files