AICollection Help

Custom marshal/unmarshal

Below is a basic implementation of a YAML marshaller and unmarshaller in Go, supporting struct tags for field mapping. It mimics the functionality of external libraries by manually handling struct reflection and basic YAML parsing.

Code Example

package main import ( "errors" "fmt" "reflect" "strings" ) // Marshal converts a struct into a YAML string func Marshal(v interface{}) (string, error) { value := reflect.ValueOf(v) if value.Kind() != reflect.Struct { return "", errors.New("marshal: input must be a struct") } var builder strings.Builder marshalStruct(value, &builder, 0) return builder.String(), nil } // Helper to recursively marshal a struct func marshalStruct(value reflect.Value, builder *strings.Builder, indentLevel int) { indent := strings.Repeat(" ", indentLevel) typ := value.Type() for i := 0; i < value.NumField(); i++ { field := value.Field(i) fieldType := typ.Field(i) // Get the YAML tag or default to the field name tag := fieldType.Tag.Get("yaml") if tag == "" { tag = fieldType.Name } // Handle struct fields if field.Kind() == reflect.Struct { builder.WriteString(fmt.Sprintf("%s%s:\n", indent, tag)) marshalStruct(field, builder, indentLevel+1) } else { builder.WriteString(fmt.Sprintf("%s%s: %v\n", indent, tag, field.Interface())) } } } // Unmarshal parses a YAML string into a struct func Unmarshal(data string, v interface{}) error { value := reflect.ValueOf(v) if value.Kind() != reflect.Ptr || value.Elem().Kind() != reflect.Struct { return errors.New("unmarshal: input must be a pointer to a struct") } lines := strings.Split(data, "\n") parseYAML(lines, value.Elem(), 0, "") return nil } // Helper to recursively parse YAML func parseYAML(lines []string, value reflect.Value, indentLevel int, parentKey string) { for _, line := range lines { trimmed := strings.TrimSpace(line) if trimmed == "" || strings.HasPrefix(trimmed, "#") { continue } parts := strings.SplitN(trimmed, ":", 2) if len(parts) < 2 { continue } key := strings.TrimSpace(parts[0]) val := strings.TrimSpace(parts[1]) // Map to struct fields for i := 0; i < value.NumField(); i++ { field := value.Field(i) fieldType := value.Type().Field(i) tag := fieldType.Tag.Get("yaml") if tag == "" { tag = fieldType.Name } if key == tag { if field.Kind() == reflect.String { field.SetString(val) } else if field.Kind() == reflect.Int { // Note: Add error handling for conversion in a real implementation field.SetInt(int64(len(val))) } else if field.Kind() == reflect.Struct { // Recursively parse structs // Note: Add implementation to parse nested maps } break } } } } // Example struct with YAML tags type Person struct { Name string `yaml:"name"` Age int `yaml:"age"` Email string `yaml:"email"` Address struct { Street string `yaml:"street"` City string `yaml:"city"` } `yaml:"address"` } func main() { // Example data person := Person{ Name: "John Doe", Age: 30, Email: "john.doe@example.com", Address: struct { Street string `yaml:"street"` City string `yaml:"city"` }{ Street: "123 Elm St", City: "Springfield", }, } // Marshal struct to YAML yamlData, err := Marshal(person) if err != nil { fmt.Println("Marshal error:", err) return } fmt.Println("Marshalled YAML:") fmt.Println(yamlData) // Unmarshal YAML to struct inputYAML := ` name: Jane Doe age: 25 email: jane.doe@example.com address: street: 456 Oak St city: Shelbyville ` var newPerson Person err = Unmarshal(inputYAML, &newPerson) if err != nil { fmt.Println("Unmarshal error:", err) return } fmt.Println("\nUnmarshalled Struct:") fmt.Printf("%+v\n", newPerson) }

Features:

  1. Struct Tags: Supports yaml tags for field mapping.

  2. Nested Structs: Handles nested structs for marshalling and basic support for unmarshalling.

  3. Manual Reflection: Uses Go's reflection package to dynamically process structs.

Limitations:

  • Data Types: Currently handles only basic types like strings and integers. You can expand it to handle floats, booleans, slices, and maps.

  • Nested Parsing: For unmarshalling, support for nested structs is limited and needs further enhancement.

  • Error Handling: Add comprehensive error checks for parsing and type conversion.

This implementation provides a starting point for a simple YAML marshaller and unmarshaller without relying on external libraries.

Last modified: 19 December 2024