Serializers Service Kind

Overview

Serializers are entity-specific services responsible for converting domain model entities into JSON format for API responses. They provide standardized JSON serialization with proper field mapping, type conversion, and support for both individual entities and entity collections.

Architecture

Core Components

JSON Utilities Library (src/lib/to_json/) - Field-specific JSON encoding functions for different data types - Proper JSON key formatting and value marshaling - Type-safe conversion utilities for common field types

Entity-Specific Serializers (src/srv/serializers/) - Each domain model has its own dedicated serializer (e.g., GardenSerializer, UserSerializer) - All serializers follow the same interface pattern and method conventions - Manual field-by-field serialization for precise control over JSON output

Key Components

Field Type Functions

The to_json library provides type-specific field encoding: - StringField(name, value): String field encoding - IntegerField(name, value): Integer field encoding - BooleanField(name, value): Boolean field encoding - TimeField(name, value): Time/date field encoding - DecimalField(name, value): Floating-point field encoding - JsonField(name, value): Raw JSON field embedding - StructField(name, value): Complex struct field encoding

Standard Methods

Every entity serializer implements these standard methods:

Single Entity Serialization

OneToJson(entity) (json.RawMessage, error)
  • Serialize individual entity to JSON format
  • Returns json.RawMessage for efficient JSON handling
  • Handles all entity fields with appropriate type conversion

Collection Serialization

SetToJson(entities) (json.RawMessage, error)
  • Serialize array of entities to JSON array format
  • Efficiently processes multiple entities
  • Maintains proper JSON array structure

Usage Examples

Single Entity Serialization

garden := &mdl.Garden{
    Id: "123",
    Name: "My Garden",
    Picture: "garden.jpg",
}

jsonData, err := gardenSerializer.OneToJson(garden)
if err != nil {
    return err
}
// Returns: {"id":"123","name":"My Garden","picture":"garden.jpg",...}

Collection Serialization

gardens := []*mdl.Garden{garden1, garden2, garden3}

jsonData, err := gardenSerializer.SetToJson(gardens)
if err != nil {
    return err
}
// Returns: [{"id":"123",...},{"id":"456",...},{"id":"789",...}]

API Response Integration

// In JSON view or API handler
gardens, err := fetcher.FindAll(ctx, nil)
if err != nil {
    return err
}

jsonResponse, err := gardenSerializer.SetToJson(gardens)
if err != nil {
    return err
}

// Send jsonResponse as HTTP response

JSON Field Construction

Serializers manually construct JSON fields for precise control:

Field Assembly Pattern

func (this *GardenSerializer) OneToJson(garden *mdl.Garden) (json.RawMessage, error) {
    var fields []string
    var field string

    field, _ = to_json.StringField("id", garden.Id)
    fields = append(fields, field)

    field, _ = to_json.TimeField("created_at", garden.CreatedAt)
    fields = append(fields, field)

    field, _ = to_json.StringField("name", garden.Name)
    fields = append(fields, field)

    return json.RawMessage("{" + strings.Join(fields, ",") + "}"), nil
}

Type-Specific Field Handling

  • String fields: Direct string encoding with proper escaping
  • Time fields: ISO 8601 timestamp formatting
  • Integer/Boolean fields: Native JSON type conversion
  • Complex fields: Struct serialization with nested JSON

Collection Processing

Collection serialization efficiently handles entity arrays:

Individual Processing

func (this *GardenSerializer) SetToJson(gardens []*mdl.Garden) (json.RawMessage, error) {
    var jsonStrings []string

    for _, garden := range gardens {
        j, err := this.OneToJson(garden)
        if err != nil {
            return json.RawMessage(""), err
        }
        jsonStrings = append(jsonStrings, string(j))
    }

    return json.RawMessage("[" + strings.Join(jsonStrings, ",") + "]"), nil
}

JSON Output Characteristics

Field Naming

  • Uses snake_case field names for JSON keys ("created_at", "updated_at")
  • Matches REST API conventions and database column naming
  • Consistent naming across all entity serializers

Type Conversion

  • Time fields: Serialized as ISO 8601 timestamps
  • String fields: Proper JSON string escaping
  • Numeric fields: Native JSON number representation
  • Boolean fields: Native JSON boolean values

Output Format

  • Single entity: JSON object {"field1":"value1","field2":"value2"}
  • Entity collection: JSON array [{...},{...},{...}]
  • Empty collection: Empty JSON array []

Integration with Framework

With JSON Views

// In JSON view services
jsonData, err := serializer.SetToJson(entities)
return jsonData  // Direct HTTP response

With API Handlers

// In API endpoint handlers
entities, err := fetcher.FindPage(ctx, mod)
jsonResponse, err := serializer.SetToJson(entities)
// Return as API response

Error Handling

  • Field encoding errors are propagated up the call stack
  • Failed individual entity serialization fails entire collection
  • JSON marshaling errors are returned with empty json.RawMessage
  • Type conversion errors handled by to_json utility functions

Performance Considerations

  • Manual field assembly: More control but potentially slower than reflection-based approaches
  • String building: Efficient string concatenation for JSON construction
  • Memory efficiency: Direct json.RawMessage usage avoids intermediate copies
  • Error short-circuiting: Early return on first serialization error