From 2c4d7c44ca38c6ae4ac2da3fa3fb47ca39009544 Mon Sep 17 00:00:00 2001 From: guonaihong Date: Fri, 20 Jan 2023 23:38:23 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + json/json.go | 124 +++++++++++++++++++++++++++++++++++---------- json/json_test.go | 27 +++++++++- testdata/test1.txt | 11 ++++ 4 files changed, 134 insertions(+), 29 deletions(-) create mode 100644 testdata/test1.txt diff --git a/.gitignore b/.gitignore index 66fd13c..2962afb 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out +/cover.cov # Dependency directories (remove the comment below to include it) # vendor/ diff --git a/json/json.go b/json/json.go index 38b7882..1ceb9f2 100644 --- a/json/json.go +++ b/json/json.go @@ -13,41 +13,92 @@ import ( ) type JSON struct { - obj interface{} // json/yaml 解成map[string]interface{} 或者[]interface{} - Indent int // 控制输出缩进 - buf bytes.Buffer // 存放内联结构体的数据 - inline bool // 是否内联 - tag string // json, yaml, 默认json - structName string //最外层结构体名 + obj interface{} // json/yaml 解成map[string]interface{} 或者[]interface{} + Indent int // 控制输出缩进 + buf bytes.Buffer // 存放内联结构体的数据 + inline bool // 是否内联 + tag string // json, yaml, 默认json + structName string // 最外层结构体名 + structBuf map[string]*bytes.Buffer // 记录拆分结构体 } +// 原始json +/* +{ + "first" : ["a", "b"], + "second" : {"b1" : "b1", "b2" : "b2"}, + "third" : [{"b1" : "b1", "b2" : "b2"}] +} +*/ + +// 生成拆开的结构体 +/* +type AutoGenerated struct { + First []string `json:"first"` + Second Second `json:"second"` + Third []Third `json:"third"` +} +type Second struct { + B1 string `json:"b1"` + B2 string `json:"b2"` +} +type Third struct { + B1 string `json:"b1"` + B2 string `json:"b2"` +} +*/ + +// 生成内联结构体 +/* + type AutoGenerated struct { + First []string `json:"first"` + Second struct { + B1 string `json:"b1"` + B2 string `json:"b2"` + } `json:"second"` + Third []struct { + B1 string `json:"b1"` + B2 string `json:"b2"` + } `json:"third"` + } +*/ + const ( structStart = "type %s struct {\n" structEnd = "}" startArrayStart = "%s []" - startInlineMap = "%s struct {\n" - endInlineMap = "} `json:\"%s\"`" + startInlineMap = "%s struct {\n" // 内联结构体开始 + endInlineMap = "} `json:\"%s\"`" // 内联结构体结束 + structMap = "%s %s `json:\"%s\"`" // 拆开结构体开始 + endMap = "}" // 拆开结构体结束 emptyMap = "%s struct {" + "} `json:\"%s\"`" + "}" - keyName = "%s %s `json:\"%s\"`" + keyName = "%s %s `json:\"%s\"`" + defStructName = "AutoGeneration" ) -// TODO: -// 分开结构体 -// 分开结构体,同名情况 -func New(jsonBytes []byte, structName string, tag string) (f *JSON, err error) { +func New(jsonBytes []byte, opt ...JSONConfig) (f *JSON, err error) { var o map[string]interface{} jsonBytes = bytes.TrimSpace(jsonBytes) + if b := Valid(jsonBytes); !b { return nil, fmt.Errorf("tostruct:Not qualified json") } var a []interface{} - rv := &JSON{} + rv := &JSON{inline: true} - rv.buf.WriteString(fmt.Sprintf(structStart, structName)) + for _, o := range opt { + o(rv) + } + + if rv.structName == "" { + rv.structName = defStructName + } + + rv.buf.WriteString(fmt.Sprintf(structStart, rv.structName)) if jsonBytes[0] == '{' { json.Unmarshal(jsonBytes, &o) rv.obj = o @@ -61,14 +112,13 @@ func New(jsonBytes []byte, structName string, tag string) (f *JSON, err error) { } func (f *JSON) Marshal() (b []byte, err error) { - f.marshalValue("", f.obj, false, 0) + f.marshalValue("", f.obj, false, 0, &f.buf) f.buf.WriteString(structEnd) return f.buf.Bytes(), nil } -func (f *JSON) marshalMap(key string, m map[string]interface{}, depth int) { +func (f *JSON) marshalMap(key string, m map[string]interface{}, depth int, buf *bytes.Buffer) { - buf := &f.buf remaining := len(m) fieldName, tagName := getFieldAndTagName(key) @@ -77,6 +127,18 @@ func (f *JSON) marshalMap(key string, m map[string]interface{}, depth int) { return } + structTypeName := fieldName + for count := 0; ; count++ { + + if _, ok := f.structBuf[structTypeName]; ok { + // 比较少见的情况, 结构体里面有重名变量 + // 使用fieldName + 数字编号的形式解决重名问题 + structTypeName = fmt.Sprintf("%s%d", fieldName, count) + continue + } + break + } + keys := make([]string, 0) for key := range m { keys = append(keys, key) @@ -85,32 +147,39 @@ func (f *JSON) marshalMap(key string, m map[string]interface{}, depth int) { sort.Strings(keys) if len(key) > 0 { - buf.WriteString(fmt.Sprintf(startInlineMap, fieldName)) + if f.inline { + buf.WriteString(fmt.Sprintf(startInlineMap, fieldName)) + } else { + + } } for _, key := range keys { f.writeIndent(buf, depth+1) - f.marshalValue(key, m[key], false, depth+1) + f.marshalValue(key, m[key], false, depth+1, buf) f.writeObjSep(buf) } f.writeIndent(buf, depth) if len(key) > 0 { - buf.WriteString(fmt.Sprintf(endInlineMap, tagName)) + if f.inline { + buf.WriteString(fmt.Sprintf(endInlineMap, tagName)) + } else { + + } } } -func (f *JSON) marshalArray(key string, a []interface{}, depth int) { - buf := &f.buf +func (f *JSON) marshalArray(key string, a []interface{}, depth int, buf *bytes.Buffer) { if len(a) == 0 { buf.WriteString(fmt.Sprintf("%s interface{} `json:\"json:%s\"`", key, key)) return } - f.marshalValue(key, a[0], true, depth) + f.marshalValue(key, a[0], true, depth, buf) } func getFieldAndTagName(key string) (string, string) { @@ -120,8 +189,7 @@ func getFieldAndTagName(key string) (string, string) { return fieldName, tagName } -func (f *JSON) marshalValue(key string, obj interface{}, fromArray bool, depth int) { - buf := &f.buf +func (f *JSON) marshalValue(key string, obj interface{}, fromArray bool, depth int, buf *bytes.Buffer) { typePrefix := "" if fromArray { typePrefix = "[]" @@ -131,9 +199,9 @@ func (f *JSON) marshalValue(key string, obj interface{}, fromArray bool, depth i switch v := obj.(type) { case map[string]interface{}: - f.marshalMap(key, v, depth) + f.marshalMap(key, v, depth, buf) case []interface{}: - f.marshalArray(key, v, depth) + f.marshalArray(key, v, depth, buf) case string: buf.WriteString(fmt.Sprintf("%s %sstring `json:\"%s\"`", fieldName, typePrefix, tagName)) case float64: diff --git a/json/json_test.go b/json/json_test.go index b3f7753..7df51ac 100644 --- a/json/json_test.go +++ b/json/json_test.go @@ -3,7 +3,9 @@ package json import ( + "bytes" "fmt" + "os" "testing" "github.com/stretchr/testify/assert" @@ -17,9 +19,32 @@ func Test_Gen_Obj_JSON(t *testing.T) { "third" : [{"b1" : "b1", "b2" : "b2"}] } ` - j, err := New([]byte(obj), "reqName", "json") + j, err := New([]byte(obj), WithStructName("reqName"), WithTagName("json")) assert.NoError(t, err) all, err := j.Marshal() assert.NoError(t, err) fmt.Println(string(all)) + + need, err := os.ReadFile("../testdata/test1.txt") + assert.NoError(t, err) + assert.Equal(t, string(bytes.TrimSpace(need)), string(all)) +} + +func Test_Gen_Obj_JSON2(t *testing.T) { + obj := ` +{ + "first" : ["a", "b"], + "second" : {"b1" : "b1", "b2" : "b2"}, + "third" : [{"b1" : "b1", "b2" : "b2"}] +} + ` + j, err := New([]byte(obj), WithStructName("reqName"), WithTagName("json"), WithNotInline()) + assert.NoError(t, err) + all, err := j.Marshal() + assert.NoError(t, err) + fmt.Println(string(all)) + + need, err := os.ReadFile("../testdata/test1.txt") + assert.NoError(t, err) + assert.Equal(t, string(bytes.TrimSpace(need)), string(all)) } diff --git a/testdata/test1.txt b/testdata/test1.txt new file mode 100644 index 0000000..790af91 --- /dev/null +++ b/testdata/test1.txt @@ -0,0 +1,11 @@ +type reqName struct { + First []string `json:"first"` + Second struct { + B1 string `json:"b1"` + B2 string `json:"b2"` + } `json:"second"` + Third struct { + B1 string `json:"b1"` + B2 string `json:"b2"` + } `json:"third"` +}