# Error Serialization Reference
This document provides detailed information about how ErrorMessage handles serialization of error messages to different formats.
## String Serialization
ErrorMessage implements the `String.Chars` protocol, which allows error messages to be converted to strings using `to_string/1` or string interpolation.
### Basic Format
The string representation of an error message follows this format:
- For errors without details: `"#{code} - #{message}"`
- For errors with details: `"#{code} - #{message}\nDetails: \n#{inspect(details, pretty: true)}"`
### Examples
```elixir
# Error without details
error = ErrorMessage.not_found("User not found")
to_string(error) # "not_found - User not found"
# Error with details
error = ErrorMessage.internal_server_error("Database error", %{table: "users", reason: :connection_lost})
to_string(error)
# "internal_server_error - Database error
# Details:
# %{reason: :connection_lost, table: "users"}"
# String interpolation
"Error occurred: #{error}"
# "Error occurred: internal_server_error - Database error
# Details:
# %{reason: :connection_lost, table: "users"}"
```
## JSON Serialization
ErrorMessage provides the `to_jsonable_map/1` function to convert error messages to maps suitable for JSON serialization.
### Basic Format
The JSON representation of an error message follows this format:
```json
{
"code": "error_code",
"message": "Error message",
"details": { ... }
}
```
If a request ID is available in the Logger metadata, it will be included:
```json
{
"code": "error_code",
"message": "Error message",
"details": { ... },
"request_id": "FzMx0iBDvDDJ-GkAAAfh"
}
```
### Data Type Handling
The ErrorMessage.Serializer module includes the `ensure_json_serializable/1` function, which handles conversion of Elixir-specific data types to JSON-compatible formats:
| Elixir Type | JSON Representation |
|-------------|---------------------|
| `Date` | ISO 8601 date string |
| `Time` | ISO 8601 time string |
| `DateTime` | ISO 8601 datetime string |
| `NaiveDateTime` | ISO 8601 datetime string |
| `Struct` | `{"struct": "StructName", "data": {...}}` |
| `Tuple` | Array |
| `PID` | String representation (with registered name if available) |
| `Function` | `{"module": "Module", "function": "function_name", "arity": n}` |
| `List` | Array with each element converted recursively |
| `Map` | Object with each value converted recursively |
| Other types | Passed through as-is |
### Examples
```elixir
# Basic error
error = ErrorMessage.not_found("User not found", %{user_id: 123})
ErrorMessage.to_jsonable_map(error)
# %{code: :not_found, message: "User not found", details: %{user_id: 123}}
# Error with complex details
error = ErrorMessage.bad_request("Invalid data", %{
date: ~D[2023-01-15],
time: ~T[14:30:00],
callback: &String.length/1,
user: %UserStruct{name: "John", created_at: ~N[2023-01-01 00:00:00]}
})
ErrorMessage.to_jsonable_map(error)
# %{
# code: :bad_request,
# message: "Invalid data",
# details: %{
# date: "2023-01-15",
# time: "14:30:00",
# callback: %{module: "Elixir.String", function: "length", arity: 1},
# user: %{
# struct: "UserStruct",
# data: %{
# name: "John",
# created_at: "2023-01-01T00:00:00"
# }
# }
# }
# }
```
### Direct JSON Encoding
If the Jason library is available, ErrorMessage automatically implements the `Jason.Encoder` protocol, allowing error messages to be directly encoded to JSON:
```elixir
# With Jason available
Jason.encode!(ErrorMessage.not_found("User not found"))
# "{\"code\":\"not_found\",\"message\":\"User not found\"}"
# With details
Jason.encode!(ErrorMessage.not_found("User not found", %{user_id: 123}))
# "{\"code\":\"not_found\",\"message\":\"User not found\",\"details\":{\"user_id\":123}}"
```
Note that when using `Jason.encode!/1` directly on an `%ErrorMessage{}` struct, the serialization of complex data types in the details field may not be handled as thoroughly as when using `ErrorMessage.to_jsonable_map/1` first. For the most reliable JSON serialization, especially with complex data structures, use:
```elixir
error
|> ErrorMessage.to_jsonable_map()
|> Jason.encode!()
```
## Integration with Phoenix
When using ErrorMessage with Phoenix, you can easily render error messages as JSON responses:
```elixir
defmodule MyApp.ErrorView do
use MyApp, :view
def render("error.json", %{error: error}) do
ErrorMessage.to_jsonable_map(error)
end
end
```
This will produce JSON responses with the appropriate structure and data type handling.