[mirror] convert json to go types olexsmir.xyz/json2go
at main 3.4 kB view raw
1package json2go 2 3import ( 4 "errors" 5 "fmt" 6 "reflect" 7 "strings" 8 "testing" 9) 10 11func field(indentLvl int, name, type_ string, json_ ...string) string { 12 indent := strings.Repeat("\t", indentLvl) 13 if strings.Contains(type_, "struct") { 14 return fmt.Sprintf("\n%s%s %s", indent, name, type_) 15 } 16 17 tag := strings.ToLower(name) 18 if len(json_) == 1 { 19 tag = json_[0] 20 } 21 return fmt.Sprintf("\n%s%s %s `json:\"%s\"`", indent, name, type_, tag) 22} 23 24func TestTransformer_Transform(t *testing.T) { 25 tests := map[string]struct { 26 input string 27 output string 28 structName string 29 err error 30 }{ 31 "simple object": { 32 input: `{"name": "Olex", "active": true, "age": 420}`, 33 output: "type Out struct {" + 34 field(1, "Active", "bool") + 35 field(1, "Age", "int") + 36 field(1, "Name", "string") + 37 "\n}", 38 }, 39 "invalid json": { 40 err: ErrInvalidJSON, 41 input: `{"invalid":json}`, 42 }, 43 "invalid struct name, starts with number": { 44 err: ErrInvalidStructName, 45 structName: "1Name", 46 }, 47 "invalid struct name, has space": { 48 err: ErrInvalidStructName, 49 structName: "Name Name2", 50 }, 51 "invalid struct name, has non letter/number": { 52 err: ErrInvalidStructName, 53 structName: "Name$", 54 }, 55 "snake_case to CamelCase": { 56 input: `{"first_name": "Bob", "last_name": "Bobberson"}`, 57 output: "type Out struct {" + 58 field(1, "FirstName", "string", "first_name") + 59 field(1, "LastName", "string", "last_name") + 60 "\n}", 61 }, 62 "nested object and array": { 63 input: `{"user": {"name": "Alice", "score": 95.5}, "tags": ["go", "json"]}`, 64 output: "type Out struct {" + 65 field(1, "Tags", "[]string") + 66 field(1, "User", "struct {") + 67 field(2, "Name", "string") + 68 field(2, "Score", "float64") + 69 "\n\t} `json:\"user\"`" + 70 "\n}", 71 }, 72 "empty nested object": { 73 input: `{"user": {}}`, 74 output: "type Out struct {" + 75 field(1, "User", "struct {") + 76 "\n\t} `json:\"user\"`" + 77 "\n}", 78 }, 79 "array of object": { 80 input: `[{"name": "John"}, {"name": "Jane"}]`, 81 output: "type Out []struct {" + 82 field(1, "Name", "string") + 83 "\n}", 84 }, 85 "empty array": { 86 input: `{"items": []}`, 87 output: "type Out struct {" + 88 field(1, "Items", "[]any") + 89 "\n}", 90 }, 91 "null": { 92 input: `{"item": null}`, 93 output: `type Out struct {` + 94 field(1, "Item", "any") + 95 "\n}", 96 }, 97 "numbers": { 98 input: `{"pos": 123, "neg": -321, "float": 420.69}`, 99 output: "type Out struct {" + 100 field(1, "Float", "float64") + 101 field(1, "Neg", "int") + 102 field(1, "Pos", "int") + 103 "\n}", 104 }, 105 } 106 107 trans := NewTransformer() 108 for tname, tt := range tests { 109 t.Run(tname, func(t *testing.T) { 110 sn := "Out" 111 if tt.structName != "" { 112 sn = tt.structName 113 } 114 115 result, err := trans.Transform(sn, tt.input) 116 assertEqualErr(t, tt.err, err) 117 assertEqual(t, tt.output, result) 118 }) 119 } 120} 121 122func assertEqualErr(t *testing.T, expected, actual error) { 123 t.Helper() 124 if expected == nil && actual == nil { 125 return 126 } 127 128 if expected == nil || actual == nil { 129 t.Errorf("expected: %v, got: %v", expected, actual) 130 return 131 } 132 133 if !errors.Is(actual, expected) { 134 t.Errorf("expected error: %v, got: %v", expected, actual) 135 } 136} 137 138func assertEqual[T any](t *testing.T, expected, actual T) { 139 t.Helper() 140 if !reflect.DeepEqual(expected, actual) { 141 t.Errorf("expected: %v, got: %v\n", expected, actual) 142 } 143}