SafeJson (fnord v0.9.29)

View Source

Thin wrapper around a JSON library.

This module is a single chokepoint for JSON serialization in the app. It:

  • Sanitizes binaries before encoding to avoid errors on invalid UTF-8.
  • Translates backend-specific encode/decode errors into stable, backend-agnostic error tuples.

Struct serialization

Structs that need JSON encoding should implement the SafeJson.Serialize protocol rather than using @derive {Jason.Encoder, ...}. This keeps JSON serialization concerns inside SafeJson and avoids leaking the backend library into consuming modules.

defimpl SafeJson.Serialize, for: MyStruct do
  def for_json(%MyStruct{name: name, age: age}) do
    %{name: name, age: age}
  end
end

Summary

Functions

Like decode/1, but strips markdown code fences and leading prose before decoding. Use when parsing LLM responses that should be JSON but may be wrapped in fences or prefixed with text.

Like decode/2, but strips markdown code fences and leading prose before decoding.

Types

decode_error()

@type decode_error() :: {:invalid_json, String.t()}

encode_error()

@type encode_error() :: {:invalid_json, String.t()}

Functions

decode(input)

@spec decode(binary()) :: {:ok, term()} | {:error, decode_error()}

decode(input, opts)

@spec decode(
  binary(),
  keyword()
) :: {:ok, term()} | {:error, decode_error()}

decode!(input)

@spec decode!(binary()) :: term()

decode!(input, opts)

@spec decode!(
  binary(),
  keyword()
) :: term()

decode_lenient(input)

@spec decode_lenient(binary() | nil) :: {:ok, term()} | {:error, decode_error()}

Like decode/1, but strips markdown code fences and leading prose before decoding. Use when parsing LLM responses that should be JSON but may be wrapped in fences or prefixed with text.

decode_lenient(input, opts)

@spec decode_lenient(
  binary() | nil,
  keyword()
) :: {:ok, term()} | {:error, decode_error()}

Like decode/2, but strips markdown code fences and leading prose before decoding.

encode(term)

@spec encode(term()) :: {:ok, String.t()} | {:error, encode_error()}

encode(term, opts)

@spec encode(
  term(),
  keyword()
) :: {:ok, String.t()} | {:error, encode_error()}

encode!(term)

@spec encode!(term()) :: String.t()

encode!(term, opts)

@spec encode!(
  term(),
  keyword()
) :: String.t()