mirror of
https://github.com/antlabs/tostruct.git
synced 2025-12-24 12:58:04 +08:00
更新
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -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/
|
||||
|
||||
124
json/json.go
124
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:
|
||||
|
||||
@@ -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
11
testdata/test1.txt
vendored
Normal 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"`
|
||||
}
|
||||
Reference in New Issue
Block a user