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.RawMessagefor 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_jsonutility 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.RawMessageusage avoids intermediate copies - Error short-circuiting: Early return on first serialization error