gorm_datatypes.go
Overview
The gorm_datatypes.go file defines custom data types that facilitate the serialization and deserialization of JSON-like data structures for storage in SQL databases using the GORM ORM library. It provides two primary custom types—stateMap and dynamicJSON—that implement the necessary interfaces to allow seamless reading and writing of JSON data to various SQL dialects supported by GORM, including PostgreSQL, MySQL, and Google Spanner.
These types abstract away database-specific JSON handling by implementing GORM's Serializer interfaces and Go's standard driver.Valuer and sql.Scanner interfaces, ensuring JSON data is encoded/decoded correctly and stored using the optimal data type for each database. This improves data integrity and portability across different database backends.
Types and Their Implementations
stateMap
stateMap is a custom type defined as an alias for map[string]any, representing a flexible JSON-like structure with string keys and arbitrary value types.
type stateMap map[string]any
Purpose
To store arbitrary JSON data in a map structure.
To handle JSON serialization and deserialization automatically when persisting to or reading from the database.
To provide database-specific data types for JSON storage.
Methods
GormDataType() string
Returns the generic fallback GORM data type for
stateMap.func (stateMap) GormDataType() string { return "text" }**GormDBDataType(db gorm.DB, field schema.Field) string
Returns the appropriate database-specific data type for the JSON stored by
stateMap, based on the configured database dialect.Supported dialects:
PostgreSQL:
"JSONB"MySQL:
"LONGTEXT"Spanner:
"STRING(MAX)"
For other databases, returns an empty string, which lets GORM fallback to defaults.
Value() (driver.Value, error)
Implements the
driver.Valuerinterface to convert thestateMapinto a JSON string for database storage.If the map is
nil, it serializes as an empty JSON object ({}) instead ofNULL.Returns the JSON marshaled string or an error.
Scan(value any) error
Implements the
sql.Scannerinterface to deserialize JSON data from the database into thestateMap.Accepts
[]byteorstringtypes from the database driver.Handles
nilby initializing as an empty map.Returns an error for unsupported types or JSON unmarshalling errors.
*GormValue(ctx context.Context, db gorm.DB) clause.Expr
Returns a GORM expression representing the JSON serialized form of the
stateMap. This is used internally by GORM when building SQL queries.
Usage Example
var sm stateMap
err := db.First(&model).Error
if err != nil {
// handle error
}
jsonValue, err := sm.Value()
if err != nil {
// handle error
}
fmt.Println("Serialized JSON:", jsonValue)
dynamicJSON
dynamicJSON is a type alias for json.RawMessage, which represents raw encoded JSON data as a byte slice.
type dynamicJSON json.RawMessage
Purpose
To represent raw JSON data without unmarshaling into Go structs or maps.
To implement the
driver.Valuerandsql.Scannerinterfaces for JSON storage.To validate incoming JSON data from the database for correctness.
Methods
Value() (driver.Value, error)
Converts the
dynamicJSONdata into a string for storing in the database.Returns
nilif the content length is zero.Returns the JSON string otherwise.
Scan(value any) error
Implements the
sql.Scannerinterface to read raw JSON from the database.Accepts
[]byteorstring.Converts empty or nil inputs to a
nilvalue.Validates the JSON data using
json.Valid.Returns detailed errors if the data cannot be unmarshaled or is invalid JSON.
String() string
Returns the JSON content as a string.
GormDataType() string
Specifies the generic GORM data type fallback as
"text".**GormDBDataType(db gorm.DB, field schema.Field) string
Provides database-specific JSON column types, similar to
stateMap:MySQL:
"LONGTEXT"PostgreSQL:
"JSONB"Spanner:
"STRING(MAX)"
*GormValue(ctx context.Context, db gorm.DB) clause.Expr
Returns a GORM expression for the JSON data or
NULLif empty, used internally for query building.
Usage Example
var dj dynamicJSON
err := db.First(&model).Error
if err != nil {
// handle error
}
fmt.Println("Raw JSON string:", dj.String())
Important Implementation Details
Both
stateMapanddynamicJSONimplement the necessary interfaces for GORM to handle JSON serialization transparently.They provide database dialect-specific mappings to optimize JSON storage in different SQL engines.
stateMapmarshals Go map structures to JSON strings, whiledynamicJSONhandles raw JSON bytes directly, providing flexibility depending on use case.Both types ensure that
NULLdatabase values are converted to empty ornilJSON representations appropriately.The
GormValuemethod returns a SQL expression that GORM uses when constructing queries, encapsulating the JSON data safely.
Interaction with Other System Components
These types are primarily used in the data management layer to store and retrieve JSON-encoded application state or dynamic JSON data in SQL databases.
They interact with GORM's ORM layer by implementing interfaces such as
driver.Valuer,sql.Scanner, and GORM's serializer interfaces likeGormDataTypeInterfaceandGormDBDataTypeInterface.This file supports smooth integration with different database backends by abstracting away dialect differences, which is critical for the persistence components of the system.
Usage of these types allows other parts of the system to work with structured or raw JSON data without concern for serialization details or database compatibility.
Structure Diagram
classDiagram
class stateMap {
+GormDataType()
+GormDBDataType(db, field)
+Value()
+Scan(value)
+GormValue(ctx, db)
}
class dynamicJSON {
+GormDataType()
+GormDBDataType(db, field)
+Value()
+Scan(value)
+String()
+GormValue(ctx, db)
}
The diagram illustrates the two main types stateMap and dynamicJSON with their primary methods responsible for JSON serialization, deserialization, and database type specification. Both types implement similar interfaces but differ in internal data representation and validation approach.