json

package
v2.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 20, 2025 License: Apache-2.0 Imports: 4 Imported by: 0

Documentation

Overview

Package json provides functional wrappers around Go's encoding/json package using Either for error handling.

This package wraps JSON marshaling and unmarshaling operations in Either monads, making it easier to compose JSON operations with other functional code and handle errors in a functional style.

Core Concepts

The json package provides type-safe JSON operations that return Either[error, A] instead of the traditional (value, error) tuple pattern. This allows for better composition with other functional operations and eliminates the need for explicit error checking at each step.

Basic Usage

// Unmarshaling JSON data
type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

data := []byte(`{"name":"Alice","age":30}`)
result := json.Unmarshal[Person](data)
// result is Either[error, Person]

// Marshaling to JSON
person := Person{Name: "Bob", Age: 25}
jsonBytes := json.Marshal(person)
// jsonBytes is Either[error, []byte]

Chaining Operations

Since Marshal and Unmarshal return Either values, they can be easily composed:

result := function.Pipe2(
    person,
    json.Marshal[Person],
    either.Chain(json.Unmarshal[Person]),
)

Type Conversion

The package provides utilities for converting between types using JSON as an intermediate format:

// Convert from one type to another via JSON (returns Either)
type Source struct { Value int }
type Target struct { Value int }

src := Source{Value: 42}
result := json.ToTypeE[Target](src)
// result is Either[error, Target]

// Convert with Option (discards error details)
maybeTarget := json.ToTypeO[Target](src)
// maybeTarget is Option[Target]

Error Handling

All operations return Either[error, A], allowing you to handle errors functionally:

result := function.Pipe1(
    json.Unmarshal[Person](data),
    either.Fold(
        func(err error) string { return "Failed: " + err.Error() },
        func(p Person) string { return "Success: " + p.Name },
    ),
)

Type Aliases

The package defines convenient type aliases:

  • Either[A] = either.Either[error, A]
  • Option[A] = option.Option[A]

These aliases simplify type signatures and make the code more readable.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Either

type Either[A any] = E.Either[error, A]

Either is a type alias for either.Either[error, A], representing a computation that may fail. By convention, Left contains an error and Right contains the successful result of type A.

func Marshal

func Marshal[A any](a A) Either[[]byte]

Marshal converts a Go value to JSON-encoded bytes and returns an Either containing the result or an error.

This function wraps the standard json.Marshal in an Either monad, converting the traditional (value, error) tuple into a functional Either type. If marshaling succeeds, it returns Right[[]byte]. If it fails, it returns Left[error].

The function uses the same encoding rules as the standard library's json.Marshal, including support for struct tags, custom MarshalJSON methods, and standard type conversions.

Example:

type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

person := Person{Name: "Bob", Age: 25}
result := json.Marshal(person)
// result is Either[error, []byte]

either.Map(func(data []byte) string {
    return string(data)
})(result)
// Returns Either[error, string] with JSON string

func MarshalIndent

func MarshalIndent[A any](a A) Either[[]byte]

MarshalIndent converts a Go value to pretty-printed JSON-encoded bytes with indentation.

This function wraps the standard json.MarshalIndent in an Either monad, converting the traditional (value, error) tuple into a functional Either type. If marshaling succeeds, it returns Right[[]byte] containing the formatted JSON. If it fails, it returns Left[error].

The function uses a default indentation of two spaces (" ") with no prefix, making the output human-readable and suitable for display, logging, or configuration files. Each JSON element begins on a new line, and nested structures are indented to show their hierarchy.

Type parameter A specifies the type of value to marshal. The type must be compatible with JSON encoding rules (same as json.Marshal).

The function uses the same encoding rules as the standard library's json.MarshalIndent, including:

  • Support for struct tags to control field names and omitempty behavior
  • Custom MarshalJSON methods for types that implement json.Marshaler
  • Standard type conversions (strings, numbers, booleans, arrays, slices, maps, structs)
  • Proper escaping of special characters in strings

Example with a simple struct:

type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

person := Person{Name: "Alice", Age: 30}
result := json.MarshalIndent(person)
// result is Either[error, []byte]

either.Map(func(data []byte) string {
    return string(data)
})(result)
// Returns Either[error, string] with formatted JSON:
// {
//   "name": "Alice",
//   "age": 30
// }

Example with nested structures:

type Address struct {
    Street string `json:"street"`
    City   string `json:"city"`
}

type Employee struct {
    Name    string  `json:"name"`
    Address Address `json:"address"`
}

emp := Employee{
    Name: "Bob",
    Address: Address{Street: "123 Main St", City: "Boston"},
}
result := json.MarshalIndent(emp)
// Produces formatted JSON:
// {
//   "name": "Bob",
//   "address": {
//     "street": "123 Main St",
//     "city": "Boston"
//   }
// }

Example with error handling:

type Config struct {
    Settings map[string]interface{} `json:"settings"`
}

config := Config{Settings: map[string]interface{}{"debug": true}}
result := json.MarshalIndent(config)

either.Fold(
    func(err error) string {
        return fmt.Sprintf("Failed to marshal: %v", err)
    },
    func(data []byte) string {
        return string(data)
    },
)(result)

Example with functional composition:

// Chain operations using Either monad
result := F.Pipe2(
    person,
    json.MarshalIndent[Person],
    either.Map(func(data []byte) string {
        return string(data)
    }),
)
// result is Either[error, string] with formatted JSON

Use MarshalIndent when you need human-readable JSON output for:

  • Configuration files that humans will read or edit
  • Debug output and logging
  • API responses for development/testing
  • Documentation examples

Use Marshal (without indentation) when:

  • Minimizing payload size is important (production APIs)
  • The JSON will be consumed by machines only
  • Performance is critical (indentation adds overhead)

func ToTypeE

func ToTypeE[A any](src any) Either[A]

ToTypeE converts a value from one type to another using JSON as an intermediate format, returning an Either that contains the converted value or an error.

This function performs a round-trip conversion: src → JSON → target type A. It's useful for converting between compatible types (e.g., map[string]any to a struct) or for deep copying values.

The conversion will fail if:

  • The source value cannot be marshaled to JSON
  • The JSON cannot be unmarshaled into the target type A
  • The JSON structure doesn't match the target type's structure

Example:

type Source struct {
    Name  string
    Value int
}

type Target struct {
    Name  string `json:"name"`
    Value int    `json:"value"`
}

src := Source{Name: "test", Value: 42}
result := json.ToTypeE[Target](src)
// result is Either[error, Target]

// Converting from map to struct
data := map[string]any{"name": "Alice", "value": 100}
person := json.ToTypeE[Target](data)

func Unmarshal

func Unmarshal[A any](data []byte) Either[A]

Unmarshal parses JSON-encoded data and returns an Either containing the decoded value or an error.

This function wraps the standard json.Unmarshal in an Either monad, converting the traditional (value, error) tuple into a functional Either type. If unmarshaling succeeds, it returns Right[A]. If it fails, it returns Left[error].

Type parameter A specifies the target type for unmarshaling. The type must be compatible with the JSON structure in the input data.

Example:

type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

data := []byte(`{"name":"Alice","age":30}`)
result := json.Unmarshal[Person](data)
// result is Either[error, Person]

either.Fold(
    func(err error) { fmt.Println("Error:", err) },
    func(p Person) { fmt.Printf("Success: %s, %d\n", p.Name, p.Age) },
)(result)

type Option

type Option[A any] = option.Option[A]

Option is a type alias for option.Option[A], representing an optional value. It can be either Some(value) or None.

func ToTypeO

func ToTypeO[A any](src any) Option[A]

ToTypeO converts a value from one type to another using JSON as an intermediate format, returning an Option that contains the converted value or None if conversion fails.

This is a convenience wrapper around ToTypeE that discards error details and returns an Option instead. Use this when you only care about success/failure and don't need the specific error message.

The conversion follows the same rules as ToTypeE, performing a round-trip through JSON.

Example:

type Config struct {
    Host string `json:"host"`
    Port int    `json:"port"`
}

data := map[string]any{"host": "localhost", "port": 8080}
maybeConfig := json.ToTypeO[Config](data)
// maybeConfig is Option[Config]

option.Fold(
    func() { fmt.Println("Conversion failed") },
    func(cfg Config) { fmt.Printf("Config: %s:%d\n", cfg.Host, cfg.Port) },
)(maybeConfig)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL