# `LangExtract.Serializer`
[🔗](https://github.com/mdepolli/lang_extract/blob/v0.10.0/lib/lang_extract/serializer.ex#L1)

Serialization and deserialization of extraction results.

Converts between LangExtract structs and plain maps/JSON for storage,
debugging, and interop with external systems. `result_to_map/2` /
`result_from_map/1` cover the full `LangExtract.Result` (spans, errors,
usage); `to_map/2` / `from_map/1` cover bare span lists (e.g. from
`LangExtract.align/3`).

Known error-reason shapes serialize as tagged maps
(`%{"tag" => "task_exit", "detail" => "timeout"}`), so a loaded
`ChunkError` distinguishes failure kinds programmatically: loaded
reasons keep their outer shape (`{:task_exit, _}`, `{:api_error,
status, _}`, bare atoms), with payloads coming back as strings where
the original term wasn't one — except the common exit atoms
(`:timeout`, `:killed`, `:shutdown`), which round-trip exactly so
`{:task_exit, :timeout}` matches the same live and loaded. Reasons
outside the known set fall back
to `%{"tag" => "other", "detail" => inspect(term)}` and load as the
bare detail string — as do plain string reasons from files written
before the tagged encoding.

# `chunk_error_to_map`

```elixir
@spec chunk_error_to_map(LangExtract.ChunkError.t()) :: map()
```

Converts a `LangExtract.ChunkError` to a plain map with string keys.

Known reason shapes become tagged maps; open terms fall back to
`%{"tag" => "other", "detail" => inspect(term)}` — either way the map
is always JSON-encodable.

# `from_map`

```elixir
@spec from_map(map()) ::
  {:ok, {String.t(), [LangExtract.Span.t()]}} | {:error, :invalid_data}
```

Converts a plain map back to extraction results.

Returns `{:error, :invalid_data}` if the shape is wrong, an extraction
has an unknown `"status"`, or field types don't match the `Span`
invariants (located spans carry integer offsets, `not_found` spans
carry `nil`).

# `load_jsonl`

```elixir
@spec load_jsonl(Path.t()) ::
  {:ok, [{String.t(), [LangExtract.Span.t()]}]}
  | {:error, File.posix() | :invalid_data}
```

Loads extraction results from a JSONL file.

# `result_from_map`

```elixir
@spec result_from_map(term()) ::
  {:ok, {String.t(), LangExtract.Result.t()}} | {:error, :invalid_data}
```

Converts a plain map back to `{source, %LangExtract.Result{}}`.

Returns `{:error, :invalid_data}` if the shape or field types are wrong —
validation is strict, so a decoded struct upholds the same invariants as
a pipeline-produced one. Tagged error reasons decode to matchable terms
(see the moduledoc); plain string reasons pass through for files written
before the tagged encoding.

# `result_to_map`

```elixir
@spec result_to_map(String.t(), LangExtract.Result.t()) :: map()
```

Converts a full `LangExtract.Result` and its source to a plain map.

The map extends `to_map/2`'s shape with `"errors"` (see
`chunk_error_to_map/1`) and `"usage"` (string-keyed token counts, or
`nil` when the run reported none).

# `save_jsonl`

```elixir
@spec save_jsonl([{String.t(), [LangExtract.Span.t()]}], Path.t()) ::
  :ok | {:error, File.posix()}
```

Saves a list of extraction results to a JSONL file.

Each element is a `{source, spans}` tuple.

# `span_to_map`

```elixir
@spec span_to_map(LangExtract.Span.t()) :: map()
```

Converts a single span to a plain map with string keys.

# `to_map`

```elixir
@spec to_map(String.t(), [LangExtract.Span.t()]) :: map()
```

Converts extraction results to a plain map.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
