diff --git a/gojson/gojson.go b/gojson/gojson.go index 7539aa5..8e5e0dc 100644 --- a/gojson/gojson.go +++ b/gojson/gojson.go @@ -63,6 +63,8 @@ var ( tags = flag.String("tags", "fmt", "comma seperated list of the tags to put on the struct, default is the same as fmt") forceFloats = flag.Bool("forcefloats", false, "[experimental] force float64 type for integral values") subStruct = flag.Bool("subStruct", false, "create types for sub-structs (default is false)") + nosort = flag.Bool("nosort", false, "dont sort fields of generated structs (default is false)") + omitempty = flag.Bool("omitempty", false, "add omitempty flag in field tag (default is false)") ) func main() { @@ -108,7 +110,7 @@ func main() { parser = ParseYaml } - if output, err := Generate(input, parser, *name, *pkg, tagList, *subStruct, convertFloats); err != nil { + if output, err := Generate(input, parser, *name, *pkg, tagList, *subStruct, convertFloats, *nosort, *omitempty); err != nil { fmt.Fprintln(os.Stderr, "error parsing", err) os.Exit(1) } else { diff --git a/json-to-array_test.go b/json-to-array_test.go index f94beaa..ccf697b 100644 --- a/json-to-array_test.go +++ b/json-to-array_test.go @@ -26,7 +26,7 @@ func TestExampleArray(t *testing.T) { t.Fatalf("error reading example_array.go: %s", err) } - actual, err := Generate(i, ParseJson, "Users", "gojson", []string{"json"}, false, true) + actual, err := Generate(i, ParseJson, "Users", "gojson", []string{"json"}, false, true, false, false) if err != nil { t.Fatal(err) } diff --git a/json-to-struct.go b/json-to-struct.go index 811ee71..d0d86b0 100644 --- a/json-to-struct.go +++ b/json-to-struct.go @@ -198,7 +198,7 @@ func readFile(input io.Reader) ([]byte, error) { } // Generate a struct definition given a JSON string representation of an object and a name structName. -func Generate(input io.Reader, parser Parser, structName, pkgName string, tags []string, subStruct bool, convertFloats bool) ([]byte, error) { +func Generate(input io.Reader, parser Parser, structName, pkgName string, tags []string, subStruct bool, convertFloats bool, nosort bool, omitempty bool) ([]byte, error) { var subStructMap map[string]string = nil if subStruct { subStructMap = make(map[string]string) @@ -220,7 +220,7 @@ func Generate(input io.Reader, parser Parser, structName, pkgName string, tags [ src := fmt.Sprintf("package %s\n\ntype %s %s\n", pkgName, structName, - typeForValue(iresult, structName, tags, subStructMap, convertFloats)) + typeForValue(iresult, structName, tags, subStructMap, convertFloats, nosort, omitempty)) formatted, err := format.Source([]byte(src)) if err != nil { err = fmt.Errorf("error formatting: %s, was formatting\n%s", err, src) @@ -233,14 +233,16 @@ func Generate(input io.Reader, parser Parser, structName, pkgName string, tags [ src := fmt.Sprintf("package %s\ntype %s %s}", pkgName, structName, - generateTypes(result, structName, tags, 0, subStructMap, convertFloats)) + generateTypes(result, structName, tags, 0, subStructMap, convertFloats, nosort, omitempty)) keys := make([]string, 0, len(subStructMap)) for key := range subStructMap { keys = append(keys, key) } - sort.Strings(keys) + if !nosort { + sort.Strings(keys) + } for _, k := range keys { src = fmt.Sprintf("%v\n\ntype %v %v", src, subStructMap[k], k) @@ -264,18 +266,20 @@ func convertKeysToStrings(obj map[interface{}]interface{}) map[string]interface{ } // Generate go struct entries for a map[string]interface{} structure -func generateTypes(obj map[string]interface{}, structName string, tags []string, depth int, subStructMap map[string]string, convertFloats bool) string { +func generateTypes(obj map[string]interface{}, structName string, tags []string, depth int, subStructMap map[string]string, convertFloats bool, nosort bool, omitempty bool) string { structure := "struct {" keys := make([]string, 0, len(obj)) for key := range obj { keys = append(keys, key) } - sort.Strings(keys) + if !nosort { + sort.Strings(keys) + } for _, key := range keys { value := obj[key] - valueType := typeForValue(value, structName, tags, subStructMap, convertFloats) + valueType := typeForValue(value, structName, tags, subStructMap, convertFloats, nosort, omitempty) //value = mergeElements(value) @@ -285,9 +289,9 @@ func generateTypes(obj map[string]interface{}, structName string, tags []string, if len(value) > 0 { sub := "" if v, ok := value[0].(map[interface{}]interface{}); ok { - sub = generateTypes(convertKeysToStrings(v), structName, tags, depth+1, subStructMap, convertFloats) + "}" + sub = generateTypes(convertKeysToStrings(v), structName, tags, depth+1, subStructMap, convertFloats, nosort, omitempty) + "}" } else if v, ok := value[0].(map[string]interface{}); ok { - sub = generateTypes(v, structName, tags, depth+1, subStructMap, convertFloats) + "}" + sub = generateTypes(v, structName, tags, depth+1, subStructMap, convertFloats, nosort, omitempty) + "}" } if sub != "" { @@ -307,7 +311,7 @@ func generateTypes(obj map[string]interface{}, structName string, tags []string, } } case map[interface{}]interface{}: - sub := generateTypes(convertKeysToStrings(value), structName, tags, depth+1, subStructMap, convertFloats) + "}" + sub := generateTypes(convertKeysToStrings(value), structName, tags, depth+1, subStructMap, convertFloats, nosort, omitempty) + "}" subName := sub if subStructMap != nil { @@ -321,7 +325,7 @@ func generateTypes(obj map[string]interface{}, structName string, tags []string, } valueType = subName case map[string]interface{}: - sub := generateTypes(value, structName, tags, depth+1, subStructMap, convertFloats) + "}" + sub := generateTypes(value, structName, tags, depth+1, subStructMap, convertFloats, nosort, omitempty) + "}" subName := sub if subStructMap != nil { @@ -341,7 +345,11 @@ func generateTypes(obj map[string]interface{}, structName string, tags []string, tagList := make([]string, 0) for _, t := range tags { - tagList = append(tagList, fmt.Sprintf("%s:\"%s\"", t, key)) + tKey := key + if omitempty { + tKey += ",omitempty" + } + tagList = append(tagList, fmt.Sprintf("%s:\"%s\"", t, tKey)) } structure += fmt.Sprintf("\n%s %s `%s`", @@ -470,7 +478,7 @@ func lintFieldName(name string) string { } // generate an appropriate struct type entry -func typeForValue(value interface{}, structName string, tags []string, subStructMap map[string]string, convertFloats bool) string { +func typeForValue(value interface{}, structName string, tags []string, subStructMap map[string]string, convertFloats bool, nosort bool, omitempty bool) string { //Check if this is an array if objects, ok := value.([]interface{}); ok { types := make(map[reflect.Type]bool, 0) @@ -478,13 +486,13 @@ func typeForValue(value interface{}, structName string, tags []string, subStruct types[reflect.TypeOf(o)] = true } if len(types) == 1 { - return "[]" + typeForValue(mergeElements(objects).([]interface{})[0], structName, tags, subStructMap, convertFloats) + return "[]" + typeForValue(mergeElements(objects).([]interface{})[0], structName, tags, subStructMap, convertFloats, nosort, omitempty) } return "[]interface{}" } else if object, ok := value.(map[interface{}]interface{}); ok { - return generateTypes(convertKeysToStrings(object), structName, tags, 0, subStructMap, convertFloats) + "}" + return generateTypes(convertKeysToStrings(object), structName, tags, 0, subStructMap, convertFloats, nosort, omitempty) + "}" } else if object, ok := value.(map[string]interface{}); ok { - return generateTypes(object, structName, tags, 0, subStructMap, convertFloats) + "}" + return generateTypes(object, structName, tags, 0, subStructMap, convertFloats, nosort, omitempty) + "}" } else if reflect.TypeOf(value) == nil { return "interface{}" } diff --git a/json-to-struct_test.go b/json-to-struct_test.go index fe725e2..0255968 100644 --- a/json-to-struct_test.go +++ b/json-to-struct_test.go @@ -12,7 +12,7 @@ import ( // It does not (yet) test for correctness of the end result func TestSimpleJson(t *testing.T) { i := strings.NewReader(`{"foo" : "bar"}`) - if _, err := Generate(i, ParseJson, "TestStruct", "gojson", []string{"json"}, false, true); err != nil { + if _, err := Generate(i, ParseJson, "TestStruct", "gojson", []string{"json"}, false, true, false, false); err != nil { t.Error("Generate() error:", err) } } @@ -20,7 +20,7 @@ func TestSimpleJson(t *testing.T) { // TestNullableJson tests that a null JSON value is handled properly func TestNullableJson(t *testing.T) { i := strings.NewReader(`{"foo" : "bar", "baz" : null}`) - if _, err := Generate(i, ParseJson, "TestStruct", "gojson", []string{"json"}, false, true); err != nil { + if _, err := Generate(i, ParseJson, "TestStruct", "gojson", []string{"json"}, false, true, false, false); err != nil { t.Error("Generate() error:", err) } } @@ -28,7 +28,7 @@ func TestNullableJson(t *testing.T) { // TestSimpleArray tests that an array without conflicting types is handled correctly func TestSimpleArray(t *testing.T) { i := strings.NewReader(`{"foo" : [{"bar": 24}, {"bar" : 42}]}`) - if _, err := Generate(i, ParseJson, "TestStruct", "gojson", []string{"json"}, false, true); err != nil { + if _, err := Generate(i, ParseJson, "TestStruct", "gojson", []string{"json"}, false, true, false, false); err != nil { t.Error("Generate() error:", err) } } @@ -36,7 +36,7 @@ func TestSimpleArray(t *testing.T) { // TestInvalidFieldChars tests that a document with invalid field chars is handled correctly func TestInvalidFieldChars(t *testing.T) { i := strings.NewReader(`{"f.o-o" : 42}`) - if _, err := Generate(i, ParseJson, "TestStruct", "gojson", []string{"json"}, false, true); err != nil { + if _, err := Generate(i, ParseJson, "TestStruct", "gojson", []string{"json"}, false, true, false, false); err != nil { t.Error("Generate() error:", err) } } @@ -80,7 +80,7 @@ func TestInferFloatInt(t *testing.T) { t.Fatalf("error reading expected_floats.go.out: %s", err) } - actual, err := Generate(f, ParseJson, "Stats", "gojson", []string{"json"}, false, true) + actual, err := Generate(f, ParseJson, "Stats", "gojson", []string{"json"}, false, true, false, false) if err != nil { t.Error(err) } @@ -104,7 +104,7 @@ func TestYamlNumbers(t *testing.T) { t.Fatalf("error reading expected_numbers.go.out: %s", err) } - actual, err := Generate(f, ParseYaml, "Stats", "gojson", []string{"yaml"}, false, false) + actual, err := Generate(f, ParseYaml, "Stats", "gojson", []string{"yaml"}, false, false, false, false) if err != nil { t.Error(err) } @@ -126,7 +126,7 @@ func TestExample(t *testing.T) { t.Error("error reading expected_output_test.go", err) } - actual, err := Generate(i, ParseJson, "User", "gojson", []string{"json"}, false, true) + actual, err := Generate(i, ParseJson, "User", "gojson", []string{"json"}, false, true, false, false) if err != nil { t.Error(err) }