all repos

json2go @ 7a527f3

convert json to go type annotations

json2go/json2go_test.go(view raw)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package json2go

import (
	"errors"
	"fmt"
	"reflect"
	"strings"
	"testing"
)

func field(name, type_ string, json_ ...string) string {
	if strings.Contains(type_, "struct") {
		return fmt.Sprintf("\n%s %s", name, type_)
	}

	tag := strings.ToLower(name)
	if len(json_) == 1 {
		tag = json_[0]
	}
	return fmt.Sprintf("\n%s %s `json:\"%s\"`", name, type_, tag)
}

func TestTransformer_Transform(t *testing.T) {
	tests := map[string]struct {
		input  string
		output string
		err    error
	}{
		"simple object": {
			input: `{"name": "Olex", "active": true, "age": 420}`,
			output: "type Out struct {" +
				field("Active", "bool") +
				field("Age", "int") +
				field("Name", "string") +
				"\n}",
		},
		"invalid json": {
			err:   ErrInvalidJSON,
			input: `{"invalid":json}`,
		},
		"snake_case to CamelCase": {
			input: `{"first_name": "Bob", "last_name": "Bobberson"}`,
			output: "type Out struct {" +
				field("FirstName", "string", "first_name") +
				field("LastName", "string", "last_name") +
				"\n}",
		},
		"nested object and array": {
			input: `{"user": {"name": "Alice", "score": 95.5}, "tags": ["go", "json"]}`,
			output: "type Out struct {" +
				field("Tags", "[]string") +
				field("User", "struct {") +
				field("Name", "string") +
				field("Score", "float64") +
				"\n} `json:\"user\"`" +
				"\n}",
		},
		"empty nested object": {
			input: `{"user": {}}`,
			output: "type Out struct {" +
				field("User", "struct {") +
				"\n} `json:\"user\"`" +
				"\n}",
		},
		"array of object": {
			input: `[{"name": "John"}, {"name": "Jane"}]`,
			output: "type Out []struct {" +
				field("Name", "string") +
				"\n}",
		},
		"empty array": {
			input: `{"items": []}`,
			output: "type Out struct {" +
				field("Items", "[]any") +
				"\n}",
		},
		"null": {
			input: `{"item": null}`,
			output: `type Out struct {` +
				field("Item", "any") +
				"\n}",
		},
		"numbers": {
			input: `{"pos": 123, "neg": -321, "float": 420.69}`,
			output: "type Out struct {" +
				field("Float", "float64") +
				field("Neg", "int") +
				field("Pos", "int") +
				"\n}",
		},
	}

	trans := NewTransformer()
	for tname, tt := range tests {
		t.Run(tname, func(t *testing.T) {
			result, err := trans.Transform("Out", tt.input)
			assertEqualErr(t, tt.err, err)
			assertEqual(t, tt.output, result)
		})
	}
}

func assertEqualErr(t *testing.T, expected, actual error) {
	t.Helper()
	if (expected != nil || actual != nil) && errors.Is(expected, actual) {
		t.Errorf("expected: %v, got: %v\n", expected, actual)
	}
}

func assertEqual[T any](t *testing.T, expected, actual T) {
	t.Helper()
	if !reflect.DeepEqual(expected, actual) {
		t.Errorf("expected: %v, got: %v\n", expected, actual)
	}
}