Transform JSON to Go structs
parent
fbb6f0b9ed
commit
749ad3e848
@ -0,0 +1,2 @@
|
||||
structify
|
||||
test.json
|
@ -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…
Reference in New Issue