package main import ( "bufio" "encoding/json" "fmt" "go/format" "io" "log" "os" "strings" ) type StructField interface { GetName() string GetType() string GetJSONTag() string String() string } type StructType struct { JSONField string Name string Children []StructField } func (s *StructType) GetJSONTag() string { return fmt.Sprintf("`json:\"%s\"`", s.JSONField) } func (s *StructType) String() string { var children []string for _, v := range s.Children { children = append(children, v.String()) } return fmt.Sprintf( "%s struct {\n%s\n}", s.Name, strings.Join(children, "\n"), ) } func (s *StructType) GetType() string { return "struct" } func (s *StructType) GetName() string { return s.Name } type SimpleType struct { JSONField string Name string Type string } func (s *SimpleType) GetJSONTag() string { return fmt.Sprintf("`json:\"%s\"`", s.JSONField) } func (s *SimpleType) String() string { return fmt.Sprintf("%s %s", s.Name, s.Type) } func (s *SimpleType) GetName() string { return s.Name } func (s *SimpleType) GetType() string { return s.Type } func (s *StructType) AddChild(name string, value json.RawMessage) { var i interface{} json.Unmarshal(value, &i) switch v := i.(type) { case float64: s.addChild(ParseNumberField(name, value)) case string, bool: s.addChild(ParseSimpleField(name, fmt.Sprintf("%T", v))) case []interface{}: // To Do: parse slices s.addChild(ParseSliceField(name, value)) case map[string]interface{}: s.addChild(ParseStruct(name, value)) default: fmt.Printf("%s %s %T\n", name, "dunno", v) } } func (s *StructType) addChild(field StructField) { s.Children = append(s.Children, field) } func ParseSliceField(name string, value json.RawMessage) *SimpleType { var s []interface{} err := json.Unmarshal(value, &s) if err != nil { log.Fatal(err) } if len(s) == 0 { return &SimpleType{JSONField: name, Name: SanitizeName(name), Type: "[]interface{}"} } sliceType := fmt.Sprintf("%T", s[0]) for _, v := range s { if sliceType != fmt.Sprintf("%T", v) { return &SimpleType{JSONField: name, Name: SanitizeName(name), Type: "[]interface{}"} } } return &SimpleType{JSONField: name, Name: SanitizeName(name), Type: fmt.Sprintf("[]%s", sliceType)} } func ParseNumberField(name string, value json.RawMessage) *SimpleType { for _, b := range value { if b == '.' { return &SimpleType{JSONField: name, Name: SanitizeName(name), Type: "float64"} } } return &SimpleType{JSONField: name, Name: SanitizeName(name), Type: "int"} } func ParseSimpleField(name, gotype string) *SimpleType { return &SimpleType{JSONField: name, Name: SanitizeName(name), Type: gotype} } func ParseStruct(name string, data []byte) *StructType { jsonMap := make(map[string]json.RawMessage) err := json.Unmarshal(data, &jsonMap) if err != nil { log.Fatal(err) } s := &StructType{JSONField: name, Name: SanitizeName(name)} for key, value := range jsonMap { s.AddChild(key, value) } return s } func SanitizeName(name string) string { return strings.ReplaceAll(strings.Title(strings.ReplaceAll(name, "_", "-")), "-", "") } func FormatStructType(s *StructType, inlineTypes bool) string { var lines []string var types []string lines = append(lines, fmt.Sprintf("type %s struct {", s.Name)) for _, c := range s.Children { switch v := c.(type) { case *SimpleType: lines = append(lines, fmt.Sprintf("%s %s", c.String(), c.GetJSONTag())) case *StructType: if inlineTypes { lines = append(lines, fmt.Sprintf("%s %s", c.String(), c.GetJSONTag())) continue } lines = append(lines, fmt.Sprintf("%s %s %s", c.GetName(), c.GetName(), c.GetJSONTag())) types = append(types, FormatStructType(v, inlineTypes)) } } lines = append(lines, "}") lines = append(lines, types...) formatted, err := format.Source([]byte(strings.Join(lines, "\n"))) if err != nil { log.Fatal(err) } return string(formatted) } func main() { log.Default().SetOutput(os.Stderr) reader := bufio.NewReader(os.Stdin) buf, err := io.ReadAll(reader) if err != nil { log.Fatal(err) } s := ParseStruct("AutoGenerated", buf) fmt.Println(FormatStructType(s, false)) }