encoding(serialization codec system)

Thread-safe serialization codec system supporting multiple formats (JSON, XML, YAML, Protocol Buffers, Form) with automatic registration and content-type handling.

The encoding module provides a thread-safe serialization codec system that supports multiple formats (JSON, XML, YAML, Protocol Buffers, Form) with automatic registration and content-type handling.

Features

  • Multiple encoding formats support (JSON, XML, YAML, Protocol Buffers, Form)
  • Thread-safe codec interface for concurrent usage
  • Automatic codec registration system
  • Content-type based codec selection
  • Protocol Buffers integration with enhanced JSON support
  • Custom marshaler/unmarshaler support
  • Extensible codec system for custom formats

Core Codec Interface

 1package main
 2
 3import (
 4    "fmt"
 5    "log"
 6
 7    "github.com/crazyfrankie/frx/encoding"
 8    _ "github.com/crazyfrankie/frx/encoding/json" // Auto-register JSON codec
 9)
10
11type User struct {
12    ID    int64  `json:"id"`
13    Name  string `json:"name"`
14    Email string `json:"email"`
15}
16
17func main() {
18    user := User{
19        ID:    1,
20        Name:  "John Doe",
21        Email: "john@example.com",
22    }
23
24    // Get codec by name
25    codec := encoding.GetCodec("json")
26    if codec == nil {
27        log.Fatal("JSON codec not found")
28    }
29
30    // Marshal data
31    data, err := codec.Marshal(user)
32    if err != nil {
33        log.Fatal(err)
34    }
35
36    fmt.Printf("Marshaled: %s\n", string(data))
37
38    // Unmarshal data
39    var decoded User
40    if err := codec.Unmarshal(data, &decoded); err != nil {
41        log.Fatal(err)
42    }
43
44    fmt.Printf("Unmarshaled: %+v\n", decoded)
45}

JSON Encoding

 1package main
 2
 3import (
 4    "fmt"
 5    "log"
 6
 7    "github.com/crazyfrankie/frx/encoding"
 8    _ "github.com/crazyfrankie/frx/encoding/json"
 9)
10
11type Product struct {
12    ID          int64   `json:"id"`
13    Name        string  `json:"name"`
14    Price       float64 `json:"price"`
15    Available   bool    `json:"available"`
16    Tags        []string `json:"tags,omitempty"`
17}
18
19func main() {
20    product := Product{
21        ID:        123,
22        Name:      "Laptop",
23        Price:     999.99,
24        Available: true,
25        Tags:      []string{"electronics", "computers"},
26    }
27
28    // JSON encoding
29    jsonCodec := encoding.GetCodec("json")
30    
31    data, err := jsonCodec.Marshal(product)
32    if err != nil {
33        log.Fatal(err)
34    }
35
36    fmt.Printf("JSON: %s\n", string(data))
37
38    // JSON decoding
39    var decoded Product
40    if err := jsonCodec.Unmarshal(data, &decoded); err != nil {
41        log.Fatal(err)
42    }
43
44    fmt.Printf("Decoded: %+v\n", decoded)
45}

Protocol Buffers Support

 1package main
 2
 3import (
 4    "fmt"
 5    "log"
 6
 7    "google.golang.org/protobuf/proto"
 8    "github.com/crazyfrankie/frx/encoding"
 9    _ "github.com/crazyfrankie/frx/encoding/json"
10)
11
12// Assuming you have a protobuf message
13// message UserProto {
14//   int64 id = 1;
15//   string name = 2;
16//   string email = 3;
17// }
18
19func main() {
20    // Create protobuf message
21    userProto := &UserProto{
22        Id:    1,
23        Name:  "John Doe",
24        Email: "john@example.com",
25    }
26
27    // JSON codec automatically handles protobuf messages
28    jsonCodec := encoding.GetCodec("json")
29    
30    // Marshal protobuf to JSON
31    data, err := jsonCodec.Marshal(userProto)
32    if err != nil {
33        log.Fatal(err)
34    }
35
36    fmt.Printf("Protobuf as JSON: %s\n", string(data))
37
38    // Unmarshal JSON back to protobuf
39    var decoded UserProto
40    if err := jsonCodec.Unmarshal(data, &decoded); err != nil {
41        log.Fatal(err)
42    }
43
44    fmt.Printf("Decoded protobuf: %+v\n", &decoded)
45}

XML Encoding

 1package main
 2
 3import (
 4    "fmt"
 5    "log"
 6
 7    "github.com/crazyfrankie/frx/encoding"
 8    _ "github.com/crazyfrankie/frx/encoding/xml"
 9)
10
11type Book struct {
12    XMLName xml.Name `xml:"book"`
13    ID      int64    `xml:"id,attr"`
14    Title   string   `xml:"title"`
15    Author  string   `xml:"author"`
16    ISBN    string   `xml:"isbn"`
17}
18
19func main() {
20    book := Book{
21        ID:     1,
22        Title:  "Go Programming",
23        Author: "John Smith",
24        ISBN:   "978-0123456789",
25    }
26
27    // XML encoding
28    xmlCodec := encoding.GetCodec("xml")
29    
30    data, err := xmlCodec.Marshal(book)
31    if err != nil {
32        log.Fatal(err)
33    }
34
35    fmt.Printf("XML: %s\n", string(data))
36
37    // XML decoding
38    var decoded Book
39    if err := xmlCodec.Unmarshal(data, &decoded); err != nil {
40        log.Fatal(err)
41    }
42
43    fmt.Printf("Decoded: %+v\n", decoded)
44}

YAML Encoding

 1package main
 2
 3import (
 4    "fmt"
 5    "log"
 6
 7    "github.com/crazyfrankie/frx/encoding"
 8    _ "github.com/crazyfrankie/frx/encoding/yaml"
 9)
10
11type Config struct {
12    Server struct {
13        Host string `yaml:"host"`
14        Port int    `yaml:"port"`
15    } `yaml:"server"`
16    Database struct {
17        Host     string `yaml:"host"`
18        Port     int    `yaml:"port"`
19        Username string `yaml:"username"`
20    } `yaml:"database"`
21}
22
23func main() {
24    config := Config{}
25    config.Server.Host = "localhost"
26    config.Server.Port = 8080
27    config.Database.Host = "db.example.com"
28    config.Database.Port = 5432
29    config.Database.Username = "admin"
30
31    // YAML encoding
32    yamlCodec := encoding.GetCodec("yaml")
33    
34    data, err := yamlCodec.Marshal(config)
35    if err != nil {
36        log.Fatal(err)
37    }
38
39    fmt.Printf("YAML:\n%s\n", string(data))
40
41    // YAML decoding
42    var decoded Config
43    if err := yamlCodec.Unmarshal(data, &decoded); err != nil {
44        log.Fatal(err)
45    }
46
47    fmt.Printf("Decoded: %+v\n", decoded)
48}

Form Encoding

 1package main
 2
 3import (
 4    "fmt"
 5    "log"
 6
 7    "github.com/crazyfrankie/frx/encoding"
 8    _ "github.com/crazyfrankie/frx/encoding/form"
 9)
10
11type LoginForm struct {
12    Username string `form:"username"`
13    Password string `form:"password"`
14    Remember bool   `form:"remember"`
15}
16
17func main() {
18    form := LoginForm{
19        Username: "john_doe",
20        Password: "secret123",
21        Remember: true,
22    }
23
24    // Form encoding
25    formCodec := encoding.GetCodec("form")
26    
27    data, err := formCodec.Marshal(form)
28    if err != nil {
29        log.Fatal(err)
30    }
31
32    fmt.Printf("Form data: %s\n", string(data))
33
34    // Form decoding
35    var decoded LoginForm
36    if err := formCodec.Unmarshal(data, &decoded); err != nil {
37        log.Fatal(err)
38    }
39
40    fmt.Printf("Decoded: %+v\n", decoded)
41}

Custom Codec Implementation

 1package main
 2
 3import (
 4    "encoding/csv"
 5    "bytes"
 6    "fmt"
 7    "reflect"
 8    "strconv"
 9
10    "github.com/crazyfrankie/frx/encoding"
11)
12
13// Custom CSV codec
14type csvCodec struct{}
15
16func (c csvCodec) Marshal(v any) ([]byte, error) {
17    // Simple CSV marshaling implementation
18    var buf bytes.Buffer
19    writer := csv.NewWriter(&buf)
20    
21    // This is a simplified example
22    // In practice, you'd need more sophisticated reflection
23    rv := reflect.ValueOf(v)
24    if rv.Kind() == reflect.Slice {
25        for i := 0; i < rv.Len(); i++ {
26            item := rv.Index(i)
27            var record []string
28            
29            // Convert struct fields to strings
30            for j := 0; j < item.NumField(); j++ {
31                field := item.Field(j)
32                record = append(record, fmt.Sprintf("%v", field.Interface()))
33            }
34            
35            writer.Write(record)
36        }
37    }
38    
39    writer.Flush()
40    return buf.Bytes(), writer.Error()
41}
42
43func (c csvCodec) Unmarshal(data []byte, v any) error {
44    // CSV unmarshaling implementation
45    reader := csv.NewReader(bytes.NewReader(data))
46    records, err := reader.ReadAll()
47    if err != nil {
48        return err
49    }
50    
51    // Simplified unmarshaling logic
52    // In practice, you'd need proper reflection handling
53    fmt.Printf("CSV records: %v\n", records)
54    return nil
55}
56
57func (c csvCodec) Name() string {
58    return "csv"
59}
60
61func main() {
62    // Register custom codec
63    encoding.RegisterCodec(csvCodec{})
64    
65    // Use custom codec
66    csvCodec := encoding.GetCodec("csv")
67    if csvCodec != nil {
68        fmt.Println("CSV codec registered successfully")
69    }
70}

Content-Type Based Codec Selection

 1package main
 2
 3import (
 4    "fmt"
 5    "net/http"
 6
 7    "github.com/crazyfrankie/frx/encoding"
 8    _ "github.com/crazyfrankie/frx/encoding/json"
 9    _ "github.com/crazyfrankie/frx/encoding/xml"
10)
11
12func handleRequest(w http.ResponseWriter, r *http.Request) {
13    contentType := r.Header.Get("Content-Type")
14    
15    var codecName string
16    switch contentType {
17    case "application/json":
18        codecName = "json"
19    case "application/xml", "text/xml":
20        codecName = "xml"
21    default:
22        codecName = "json" // default
23    }
24    
25    codec := encoding.GetCodec(codecName)
26    if codec == nil {
27        http.Error(w, "Unsupported content type", http.StatusBadRequest)
28        return
29    }
30    
31    // Use codec for request/response handling
32    fmt.Printf("Using codec: %s\n", codec.Name())
33}