This commit is contained in:
guonaihong
2023-01-20 23:38:23 +08:00
parent f7e5e3fa45
commit 2c4d7c44ca
4 changed files with 134 additions and 29 deletions

1
.gitignore vendored
View File

@@ -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/

View File

@@ -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:

View File

@@ -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))
}

11
testdata/test1.txt vendored Normal file
View File

@@ -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"`
}