Transform JSON to Go structs

master
mvh 1 year ago
parent fbb6f0b9ed
commit 749ad3e848

2
.gitignore vendored

@ -0,0 +1,2 @@
structify
test.json

@ -0,0 +1,3 @@
module git.snorba.art/max/structify
go 1.18

@ -0,0 +1,189 @@
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))
}
Loading…
Cancel
Save