Complete guide for using SlimJSON as a Go library in your applications.
Table of Contents
- Installation
- Quick Start
- Configuration
- Built-in Profiles
- Custom Profiles from File
- Real-World Examples
- Best Practices
Installation
go get github.com/tradik/slimjson
Quick Start
Basic Example
package main
import (
"encoding/json"
"fmt"
"github.com/tradik/slimjson"
)
func main() {
// Your data
data := map[string]interface{}{
"user": map[string]interface{}{
"id": 123,
"name": "John Doe",
"email": "john@example.com",
"metadata": map[string]interface{}{
"created_at": "2024-01-01",
"updated_at": "2024-01-15",
},
},
"items": []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
}
// Configure compression
cfg := slimjson.Config{
MaxDepth: 2,
MaxListLength: 5,
StripEmpty: true,
}
// Process
slimmer := slimjson.New(cfg)
result := slimmer.Slim(data)
// Output
output, _ := json.MarshalIndent(result, "", " ")
fmt.Println(string(output))
}
Configuration
Config Structure
type Config struct {
// Basic Options
MaxDepth int // Maximum nesting depth (0 = unlimited)
MaxListLength int // Maximum array length (0 = unlimited)
MaxStringLength int // Maximum string length (0 = unlimited)
StripEmpty bool // Remove nulls, empty strings, empty arrays/objects
BlockList []string // Field names to remove (case-insensitive)
// Optimization Options
DecimalPlaces int // Round floats (-1 = no rounding)
DeduplicateArrays bool // Remove duplicate array values
SampleStrategy string // "none", "first_last", "random", "representative"
SampleSize int // Items to keep when sampling
// Advanced Compression
NullCompression bool // Track removed nulls in _nulls
TypeInference bool // Convert arrays to schema+data
BoolCompression bool // Convert booleans to bit flags
TimestampCompression bool // Convert ISO to unix timestamps
StringPooling bool // Deduplicate repeated strings
StringPoolMinOccurrences int // Min occurrences for pooling (default: 2)
NumberDeltaEncoding bool // Delta encoding for sequences
NumberDeltaThreshold int // Min array size for delta (default: 5)
EnumDetection bool // Convert categorical values to enums
EnumMaxValues int // Max unique values for enum (default: 10)
}
Configuration Examples
Minimal Configuration
cfg := slimjson.Config{
MaxDepth: 5,
StripEmpty: true,
}
Balanced Configuration
cfg := slimjson.Config{
MaxDepth: 5,
MaxListLength: 20,
StripEmpty: true,
DecimalPlaces: 2,
DeduplicateArrays: true,
}
Maximum Compression
cfg := slimjson.Config{
MaxDepth: 3,
MaxListLength: 10,
StripEmpty: true,
DecimalPlaces: 2,
DeduplicateArrays: true,
SampleStrategy: "representative",
SampleSize: 20,
NullCompression: true,
TypeInference: true,
BoolCompression: true,
TimestampCompression: true,
StringPooling: true,
StringPoolMinOccurrences: 2,
NumberDeltaEncoding: true,
NumberDeltaThreshold: 5,
EnumDetection: true,
EnumMaxValues: 10,
BlockList: []string{"metadata", "debug", "trace"},
}
Built-in Profiles
Using Built-in Profiles
package main
import (
"github.com/tradik/slimjson"
)
func main() {
// Get all built-in profiles
profiles := slimjson.GetBuiltinProfiles()
// Available profiles: light, medium, aggressive, ai-optimized
lightCfg := profiles["light"]
mediumCfg := profiles["medium"]
aggressiveCfg := profiles["aggressive"]
aiCfg := profiles["ai-optimized"]
// Use a profile
slimmer := slimjson.New(mediumCfg)
result := slimmer.Slim(data)
}
Profile Characteristics
// Light - Preserve most data
profiles["light"] = Config{
MaxDepth: 10,
MaxListLength: 20,
StripEmpty: true,
}
// Medium - Balanced compression
profiles["medium"] = Config{
MaxDepth: 5,
MaxListLength: 10,
StripEmpty: true,
}
// Aggressive - Maximum reduction
profiles["aggressive"] = Config{
MaxDepth: 3,
MaxListLength: 5,
StripEmpty: true,
BlockList: []string{"description", "summary", "comment", "notes", "bio", "readme"},
}
// AI-Optimized - For LLM contexts
profiles["ai-optimized"] = Config{
MaxDepth: 4,
MaxListLength: 8,
StripEmpty: true,
BlockList: []string{"avatar_url", "gravatar_id", "url", "html_url", "*_url"},
}
Custom Profiles from File
Loading .slimjson File
package main
import (
"fmt"
"github.com/tradik/slimjson"
)
func main() {
// Load from .slimjson in current dir or home dir
customProfiles, err := slimjson.LoadConfigFile()
if err != nil {
fmt.Printf("Warning: %v\n", err)
customProfiles = make(map[string]slimjson.Config)
}
// Use custom profile
if cfg, ok := customProfiles["my-api-profile"]; ok {
slimmer := slimjson.New(cfg)
result := slimmer.Slim(data)
}
}
Parsing Specific File
package main
import (
"github.com/tradik/slimjson"
)
func main() {
// Parse specific config file
profiles, err := slimjson.ParseConfigFile("/etc/myapp/.slimjson")
if err != nil {
panic(err)
}
cfg := profiles["production"]
slimmer := slimjson.New(cfg)
result := slimmer.Slim(data)
}
Combining Built-in and Custom Profiles
package main
import (
"github.com/tradik/slimjson"
)
func main() {
// Start with built-in profiles
allProfiles := slimjson.GetBuiltinProfiles()
// Load custom profiles
customProfiles, _ := slimjson.LoadConfigFile()
// Merge (custom profiles override built-in)
for name, cfg := range customProfiles {
allProfiles[name] = cfg
}
// Now use any profile
cfg := allProfiles["my-custom-profile"]
slimmer := slimjson.New(cfg)
result := slimmer.Slim(data)
}
Real-World Examples
Example 1: REST API Response Compression
package main
import (
"encoding/json"
"net/http"
"github.com/tradik/slimjson"
)
// Middleware for response compression
func CompressMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Check if client wants compressed response
if r.Header.Get("X-Compress-Response") != "true" {
next.ServeHTTP(w, r)
return
}
// Capture response
rec := &ResponseRecorder{ResponseWriter: w}
next.ServeHTTP(rec, r)
// Compress if JSON
if rec.ContentType == "application/json" {
var data interface{}
json.Unmarshal(rec.Body, &data)
cfg := slimjson.Config{
MaxDepth: 5,
MaxListLength: 50,
StripEmpty: true,
DecimalPlaces: 2,
DeduplicateArrays: true,
BlockList: []string{"internal_id", "debug"},
}
slimmer := slimjson.New(cfg)
compressed := slimmer.Slim(data)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(compressed)
}
})
}
Example 2: Database Export Optimization
package main
import (
"database/sql"
"encoding/json"
"github.com/tradik/slimjson"
)
func ExportTableToJSON(db *sql.DB, tableName string) ([]byte, error) {
// Query database
rows, err := db.Query("SELECT * FROM " + tableName)
if err != nil {
return nil, err
}
defer rows.Close()
// Convert to slice of maps
var records []map[string]interface{}
// ... populate records from rows ...
// Compress for export
cfg := slimjson.Config{
MaxDepth: 10,
MaxListLength: 1000,
StripEmpty: true,
DecimalPlaces: 4,
TypeInference: true,
NumberDeltaEncoding: true,
}
data := map[string]interface{}{
"table": tableName,
"records": records,
}
slimmer := slimjson.New(cfg)
compressed := slimmer.Slim(data)
return json.Marshal(compressed)
}
Example 3: LLM Context Preparation
package main
import (
"encoding/json"
"github.com/tradik/slimjson"
)
type LLMContext struct {
SystemPrompt string
UserData interface{}
History []interface{}
}
func PrepareLLMContext(ctx LLMContext) (string, error) {
// Combine all context data
data := map[string]interface{}{
"system": ctx.SystemPrompt,
"user": ctx.UserData,
"history": ctx.History,
}
// Maximum compression for token efficiency
cfg := slimjson.Config{
MaxDepth: 4,
MaxListLength: 15,
StripEmpty: true,
DecimalPlaces: 2,
DeduplicateArrays: true,
SampleStrategy: "representative",
SampleSize: 10,
StringPooling: true,
TypeInference: true,
BoolCompression: true,
TimestampCompression: true,
BlockList: []string{"avatar_url", "url", "html_url", "metadata"},
}
slimmer := slimjson.New(cfg)
compressed := slimmer.Slim(data)
jsonBytes, err := json.Marshal(compressed)
if err != nil {
return "", err
}
return string(jsonBytes), nil
}
Example 4: Analytics Data Processing
package main
import (
"encoding/json"
"github.com/tradik/slimjson"
)
type AnalyticsEvent struct {
Timestamp string
UserID int
Event string
Data map[string]interface{}
}
func ProcessAnalytics(events []AnalyticsEvent) ([]byte, error) {
// Convert to interface slice
data := make([]interface{}, len(events))
for i, e := range events {
data[i] = map[string]interface{}{
"timestamp": e.Timestamp,
"user_id": e.UserID,
"event": e.Event,
"data": e.Data,
}
}
// Compress with sampling for large datasets
cfg := slimjson.Config{
MaxDepth: 3,
MaxListLength: 100,
StripEmpty: true,
DecimalPlaces: 2,
SampleStrategy: "representative",
SampleSize: 50,
TypeInference: true,
TimestampCompression: true,
EnumDetection: true,
}
wrapper := map[string]interface{}{
"events": data,
}
slimmer := slimjson.New(cfg)
compressed := slimmer.Slim(wrapper)
return json.Marshal(compressed)
}
Example 5: Configuration Management
package main
import (
"github.com/tradik/slimjson"
)
type AppConfig struct {
profiles map[string]slimjson.Config
}
func NewAppConfig() *AppConfig {
ac := &AppConfig{
profiles: slimjson.GetBuiltinProfiles(),
}
// Load custom profiles
customProfiles, _ := slimjson.LoadConfigFile()
for name, cfg := range customProfiles {
ac.profiles[name] = cfg
}
return ac
}
func (ac *AppConfig) GetProfile(name string) (slimjson.Config, bool) {
cfg, ok := ac.profiles[name]
return cfg, ok
}
func (ac *AppConfig) Compress(data interface{}, profileName string) interface{} {
cfg, ok := ac.GetProfile(profileName)
if !ok {
// Fallback to medium profile
cfg = ac.profiles["medium"]
}
slimmer := slimjson.New(cfg)
return slimmer.Slim(data)
}
Best Practices
1. Choose the Right Profile
// For API responses - preserve structure
cfg := profiles["light"]
// For LLM contexts - maximize compression
cfg := profiles["ai-optimized"]
// For analytics - balance size and detail
cfg := profiles["medium"]
2. Test Compression Results
func TestCompression(data interface{}, cfg slimjson.Config) {
original, _ := json.Marshal(data)
slimmer := slimjson.New(cfg)
compressed := slimmer.Slim(data)
result, _ := json.Marshal(compressed)
ratio := float64(len(result)) / float64(len(original)) * 100
fmt.Printf("Compression: %.1f%% of original size\n", ratio)
}
3. Handle Metadata Fields
result := slimmer.Slim(data)
// Check for metadata
if resultMap, ok := result.(map[string]interface{}); ok {
if strings, ok := resultMap["_strings"]; ok {
fmt.Printf("String pool: %v\n", strings)
}
if nulls, ok := resultMap["_nulls"]; ok {
fmt.Printf("Removed nulls: %v\n", nulls)
}
}
4. Error Handling
func SafeCompress(data interface{}, profileName string) (interface{}, error) {
profiles, err := slimjson.LoadConfigFile()
if err != nil {
// Fallback to built-in profiles
profiles = slimjson.GetBuiltinProfiles()
}
cfg, ok := profiles[profileName]
if !ok {
return nil, fmt.Errorf("profile not found: %s", profileName)
}
slimmer := slimjson.New(cfg)
return slimmer.Slim(data), nil
}
5. Performance Considerations
// Reuse slimmer for same configuration
slimmer := slimjson.New(cfg)
for _, item := range items {
result := slimmer.Slim(item)
// Process result...
}
Additional Resources
- Main README - CLI usage and installation
- Examples - CLI examples and patterns
- .slimjson.example - Configuration file examples
- API Documentation - Full API reference