package dto import ( "encoding/json" "fmt" "strconv" ) // ID accepts legacy JSON integers and decimal strings. Its wire representation // is always a string so a browser can round-trip all uint64 values exactly. type ID uint64 func (id ID) MarshalJSON() ([]byte, error) { return json.Marshal(strconv.FormatUint(uint64(id), 10)) } func (id *ID) UnmarshalJSON(raw []byte) error { text := string(raw) if len(raw) > 0 && raw[0] == '"' { if err := json.Unmarshal(raw, &text); err != nil { return err } } if text == "" { return fmt.Errorf("ID must be a decimal integer") } for _, digit := range text { if digit < '0' || digit > '9' { return fmt.Errorf("ID must be a decimal integer") } } value, err := strconv.ParseUint(text, 10, 64) if err != nil { return fmt.Errorf("ID is outside the uint64 range") } *id = ID(value) return nil }