3 files changed,
61 insertions(+),
81 deletions(-)
Author:
Oleksandr Smirnov
olexsmir@gmail.com
Committed at:
2026-07-11 13:56:41 +0300
Authored at:
2026-07-11 13:56:30 +0300
Change ID:
ktnmmslzwnlpwqlorwrnqxknyxwnmyky
Parent:
c2472d6
jump to
| M | json2go_test.go |
| M | transpiler.go |
| M | transpiler_test.go |
M
json2go_test.go
··· 81 81 if !strings.Contains(result, "Tags []string") { 82 82 t.Errorf("missing Tags field") 83 83 } 84 - if !strings.Contains(result, "User OutUser") { 85 - t.Errorf("missing User field") 84 + if !strings.Contains(result, "User struct {") { 85 + t.Errorf("missing inline User struct") 86 86 } 87 - if !strings.Contains(result, "type OutUser struct") { 88 - t.Errorf("missing OutUser struct") 87 + if !strings.Contains(result, "Name string `json:\"name\"`") { 88 + t.Errorf("missing Name field in inline struct") 89 89 } 90 90 }, 91 91 }, ··· 95 95 if !strings.Contains(result, "type Out struct") { 96 96 t.Errorf("missing Out struct") 97 97 } 98 - if !strings.Contains(result, "type OutUser struct") { 99 - t.Errorf("missing OutUser struct") 98 + if !strings.Contains(result, "User struct {") { 99 + t.Errorf("missing inline User struct") 100 100 } 101 101 }, 102 102 }, 103 103 "array of object": { 104 104 input: `[{"name": "John"}, {"name": "Jane"}]`, 105 105 check: func(t *testing.T, result string) { 106 - if !strings.Contains(result, "type Out []OutItem") { 107 - t.Errorf("missing Out array type") 106 + if !strings.Contains(result, "type Out []struct {") { 107 + t.Errorf("missing Out array type with inline struct, got: %s", result) 108 108 } 109 - if !strings.Contains(result, "type OutItem struct") { 110 - t.Errorf("missing OutItem struct") 109 + if !strings.Contains(result, "Name string `json:\"name\"`") { 110 + t.Errorf("missing Name field") 111 111 } 112 112 }, 113 113 },
M
transpiler.go
··· 11 11 func NewTranspiler() *Transpiler { return &Transpiler{} } 12 12 13 13 // Transpile converts a [Value] AST to Go type definitions. 14 -// Nested types are emitted after the parent struct. 15 14 func (t *Transpiler) Transpile(structName string, v Value, includeTags bool) (string, error) { 16 15 var buf strings.Builder 17 - var nested strings.Builder 16 + buf.WriteString("type ") 17 + buf.WriteString(structName) 18 18 19 19 switch v.Kind { 20 20 case ArrayValue: 21 + buf.WriteString(" [") 21 22 if len(v.Array) == 0 { 22 - buf.WriteString("type ") 23 - buf.WriteString(structName) 24 - buf.WriteString(" []any") 23 + buf.WriteString("]any") 25 24 } else { 26 - itemType := t.writeWithNested(&buf, &nested, structName+"Item", v.Array[0], includeTags) 27 - buf.WriteString("type ") 28 - buf.WriteString(structName) 29 - buf.WriteString(" []") 30 - buf.WriteString(itemType) 25 + buf.WriteByte(']') 26 + t.writeInlineType(&buf, structName+"Item", v.Array[0], includeTags) 31 27 } 32 28 33 29 case ObjectValue: 34 - t.writeStructDef(&buf, &nested, structName, v.Object, includeTags) 30 + buf.WriteByte(' ') 31 + t.writeInlineStruct(&buf, structName, v.Object, includeTags) 35 32 36 33 default: 37 - scalarType := t.inferType(structName, v) 38 - buf.WriteString("type ") 39 - buf.WriteString(structName) 40 34 buf.WriteByte(' ') 41 - buf.WriteString(scalarType) 35 + t.writeScalarType(&buf, v) 42 36 } 43 37 44 - if nested.Len() > 0 && buf.Len() > 0 { 45 - buf.WriteString("\n\n") 46 - buf.WriteString(nested.String()) 47 - } 48 38 return buf.String(), nil 49 39 } 50 40 51 -func (t *Transpiler) writeWithNested(buf, nested *strings.Builder, name string, v Value, includeTags bool) string { 41 +func (t *Transpiler) writeInlineType(buf *strings.Builder, name string, v Value, includeTags bool) { 52 42 switch v.Kind { 53 43 case ObjectValue: 54 - t.writeStructDef(nested, nested, name, v.Object, includeTags) 55 - return name 44 + t.writeInlineStruct(buf, name, v.Object, includeTags) 56 45 57 46 case ArrayValue: 47 + buf.WriteByte('[') 58 48 if len(v.Array) == 0 { 59 - return "[]any" 49 + buf.WriteString("]any") 50 + } else { 51 + buf.WriteByte(']') 52 + t.writeInlineType(buf, name+"Item", v.Array[0], includeTags) 60 53 } 61 - itemType := t.writeWithNested(buf, nested, name+"Item", v.Array[0], includeTags) 62 - return "[]" + itemType 54 + 55 + default: 56 + t.writeScalarType(buf, v) 63 57 } 64 - return t.inferType(name, v) 65 58 } 66 59 67 -func (t *Transpiler) writeStructDef(buf, nested *strings.Builder, name string, fields []Field, includeTags bool) { 68 - if buf.Len() > 0 { 69 - buf.WriteString("\n\n") 70 - } 71 - 72 - buf.WriteString("type ") 73 - buf.WriteString(name) 74 - buf.WriteString(" struct {\n") 60 +func (t *Transpiler) writeInlineStruct(buf *strings.Builder, name string, fields []Field, includeTags bool) { 61 + buf.WriteString("struct {\n") 75 62 for _, f := range fields { 76 63 fieldName := t.sanitizeFieldName(f.K) 77 - fieldType := t.writeWithNested(buf, nested, name+fieldName, f.V, includeTags) 64 + buf.WriteByte('\t') 65 + buf.WriteString(fieldName) 66 + buf.WriteByte(' ') 67 + t.writeInlineType(buf, name+fieldName, f.V, includeTags) 78 68 if includeTags { 79 - buf.WriteByte('\t') 80 - buf.WriteString(fieldName) 81 - buf.WriteByte(' ') 82 - buf.WriteString(fieldType) 83 69 buf.WriteString(" `json:\"") 84 70 buf.WriteString(f.K) 85 - buf.WriteString("\"`\n") 86 - } else { 87 - buf.WriteByte('\t') 88 - buf.WriteString(fieldName) 89 - buf.WriteByte(' ') 90 - buf.WriteString(fieldType) 91 - buf.WriteByte('\n') 71 + buf.WriteString("\"`") 92 72 } 73 + buf.WriteByte('\n') 93 74 } 94 - buf.WriteString("}") 75 + buf.WriteByte('}') 95 76 } 96 77 97 -func (t *Transpiler) inferType(name string, v Value) string { 78 +func (t *Transpiler) writeScalarType(buf *strings.Builder, v Value) { 98 79 switch v.Kind { 99 - case ObjectValue: 100 - return name 101 - case ArrayValue: 102 - if len(v.Array) == 0 { 103 - return "[]any" 104 - } 105 - itemType := t.inferType(name+"Item", v.Array[0]) 106 - return "[]" + itemType 107 80 case StringValue: 108 - return "string" 81 + buf.WriteString("string") 109 82 case NumberValue: 110 - return "int" 83 + buf.WriteString("int") 111 84 case DecimalValue: 112 - return "float64" 85 + buf.WriteString("float64") 113 86 case BoolValue: 114 - return "bool" 115 - case NullValue: 116 - return "any" 87 + buf.WriteString("bool") 117 88 default: 118 - return "any" 89 + buf.WriteString("any") 119 90 } 120 91 } 121 92
M
transpiler_test.go
··· 51 51 if !strings.Contains(result, "type Response struct") { 52 52 t.Errorf("missing Response struct") 53 53 } 54 - if !strings.Contains(result, "type ResponseUser struct") { 55 - t.Errorf("missing ResponseUser struct") 54 + if !strings.Contains(result, "Name string `json:\"name\"`") { 55 + t.Errorf("missing Name field") 56 + } 57 + if !strings.Contains(result, "Active bool `json:\"active\"`") { 58 + t.Errorf("missing Active field") 56 59 } 57 - if !strings.Contains(result, "User ResponseUser `json:\"user\"`") { 58 - t.Errorf("missing User field") 60 + if !strings.Contains(result, "User struct {") { 61 + t.Errorf("missing inline User struct") 59 62 } 60 63 }, 61 64 }, ··· 84 87 }}, 85 88 }, 86 89 check: func(t *testing.T, result string) { 87 - if !strings.Contains(result, "type UsersItem struct") { 88 - t.Errorf("missing UsersItem struct in: %s", result) 90 + if !strings.Contains(result, "type Users []struct {") { 91 + t.Errorf("missing Users array type with inline struct, got: %s", result) 89 92 } 90 93 if !strings.Contains(result, "Id int `json:\"id\"`") { 91 94 t.Errorf("missing Id field") ··· 171 174 if !strings.Contains(result, "type Response struct") { 172 175 t.Errorf("missing Response struct") 173 176 } 174 - if !strings.Contains(result, "type ResponseUser struct") { 175 - t.Errorf("missing ResponseUser struct") 177 + if !strings.Contains(result, "User struct {") { 178 + t.Errorf("missing inline User struct") 179 + } 180 + if !strings.Contains(result, "Name string\n") { 181 + t.Errorf("missing Name field") 182 + } 183 + if !strings.Contains(result, "Active bool\n") { 184 + t.Errorf("missing Active field") 176 185 } 177 186 if strings.Contains(result, "`json:") { 178 187 t.Errorf("should not have json tags")