增加go json
This commit is contained in:
		
							
								
								
									
										497
									
								
								tool/gojson/json-to-struct.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										497
									
								
								tool/gojson/json-to-struct.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,497 @@ | ||||
| // Package gojson ... | ||||
| // | ||||
| // Description : json_tool ... | ||||
| // | ||||
| // Author : go_developer@163.com<张德满> | ||||
| // | ||||
| // Date : 2022-01-09 10:48 PM | ||||
| package gojson | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"go/format" | ||||
| 	"io" | ||||
| 	"math" | ||||
| 	"reflect" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"unicode" | ||||
|  | ||||
| 	"gopkg.in/yaml.v3" | ||||
| ) | ||||
|  | ||||
| var ForceFloats bool | ||||
|  | ||||
| // commonInitialisms is a set of common initialisms. | ||||
| // Only add entries that are highly unlikely to be non-initialisms. | ||||
| // For instance, "ID" is fine (Freudian code is rare), but "AND" is not. | ||||
| var commonInitialisms = map[string]bool{ | ||||
| 	"API":   true, | ||||
| 	"ASCII": true, | ||||
| 	"CPU":   true, | ||||
| 	"CSS":   true, | ||||
| 	"DNS":   true, | ||||
| 	"EOF":   true, | ||||
| 	"GUID":  true, | ||||
| 	"HTML":  true, | ||||
| 	"HTTP":  true, | ||||
| 	"HTTPS": true, | ||||
| 	"ID":    true, | ||||
| 	"IP":    true, | ||||
| 	"JSON":  true, | ||||
| 	"LHS":   true, | ||||
| 	"QPS":   true, | ||||
| 	"RAM":   true, | ||||
| 	"RHS":   true, | ||||
| 	"RPC":   true, | ||||
| 	"SLA":   true, | ||||
| 	"SMTP":  true, | ||||
| 	"SSH":   true, | ||||
| 	"TLS":   true, | ||||
| 	"TTL":   true, | ||||
| 	"UI":    true, | ||||
| 	"UID":   true, | ||||
| 	"UUID":  true, | ||||
| 	"URI":   true, | ||||
| 	"URL":   true, | ||||
| 	"UTF8":  true, | ||||
| 	"VM":    true, | ||||
| 	"XML":   true, | ||||
| 	"NTP":   true, | ||||
| 	"DB":    true, | ||||
| } | ||||
|  | ||||
| var intToWordMap = []string{ | ||||
| 	"zero", | ||||
| 	"one", | ||||
| 	"two", | ||||
| 	"three", | ||||
| 	"four", | ||||
| 	"five", | ||||
| 	"six", | ||||
| 	"seven", | ||||
| 	"eight", | ||||
| 	"nine", | ||||
| } | ||||
|  | ||||
| type Parser func(io.Reader) (interface{}, error) | ||||
|  | ||||
| func ParseJson(input io.Reader) (interface{}, error) { | ||||
| 	var result interface{} | ||||
| 	if err := json.NewDecoder(input).Decode(&result); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return result, nil | ||||
| } | ||||
|  | ||||
| func ParseYaml(input io.Reader) (interface{}, error) { | ||||
| 	var result interface{} | ||||
| 	b, err := readFile(input) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if err := yaml.Unmarshal(b, &result); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return result, nil | ||||
| } | ||||
|  | ||||
| func readFile(input io.Reader) ([]byte, error) { | ||||
| 	buf := bytes.NewBuffer(nil) | ||||
| 	_, err := io.Copy(buf, input) | ||||
| 	if err != nil { | ||||
| 		return []byte{}, nil | ||||
| 	} | ||||
| 	return buf.Bytes(), nil | ||||
| } | ||||
|  | ||||
| // 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) { | ||||
| 	var subStructMap map[string]string = nil | ||||
| 	if subStruct { | ||||
| 		subStructMap = make(map[string]string) | ||||
| 	} | ||||
|  | ||||
| 	var result map[string]interface{} | ||||
|  | ||||
| 	iresult, err := parser(input) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	switch iresult := iresult.(type) { | ||||
| 	case map[interface{}]interface{}: | ||||
| 		result = convertKeysToStrings(iresult) | ||||
| 	case map[string]interface{}: | ||||
| 		result = iresult | ||||
| 	case []interface{}: | ||||
| 		src := fmt.Sprintf("package %s\n\ntype %s %s\n", | ||||
| 			pkgName, | ||||
| 			structName, | ||||
| 			typeForValue(iresult, structName, tags, subStructMap, convertFloats)) | ||||
| 		formatted, err := format.Source([]byte(src)) | ||||
| 		if err != nil { | ||||
| 			err = fmt.Errorf("error formatting: %s, was formatting\n%s", err, src) | ||||
| 		} | ||||
| 		return formatted, err | ||||
| 	default: | ||||
| 		return nil, fmt.Errorf("unexpected type: %T", iresult) | ||||
| 	} | ||||
|  | ||||
| 	src := fmt.Sprintf("package %s\ntype %s %s}", | ||||
| 		pkgName, | ||||
| 		structName, | ||||
| 		generateTypes(result, structName, tags, 0, subStructMap, convertFloats)) | ||||
|  | ||||
| 	keys := make([]string, 0, len(subStructMap)) | ||||
| 	for key := range subStructMap { | ||||
| 		keys = append(keys, key) | ||||
| 	} | ||||
|  | ||||
| 	sort.Strings(keys) | ||||
|  | ||||
| 	for _, k := range keys { | ||||
| 		src = fmt.Sprintf("%v\n\ntype %v %v", src, subStructMap[k], k) | ||||
| 	} | ||||
|  | ||||
| 	formatted, err := format.Source([]byte(src)) | ||||
| 	if err != nil { | ||||
| 		err = fmt.Errorf("error formatting: %s, was formatting\n%s", err, src) | ||||
| 	} | ||||
| 	return formatted, err | ||||
| } | ||||
|  | ||||
| func convertKeysToStrings(obj map[interface{}]interface{}) map[string]interface{} { | ||||
| 	res := make(map[string]interface{}) | ||||
|  | ||||
| 	for k, v := range obj { | ||||
| 		res[fmt.Sprintf("%v", k)] = v | ||||
| 	} | ||||
|  | ||||
| 	return res | ||||
| } | ||||
|  | ||||
| // 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 { | ||||
| 	structure := "struct {" | ||||
|  | ||||
| 	keys := make([]string, 0, len(obj)) | ||||
| 	for key := range obj { | ||||
| 		keys = append(keys, key) | ||||
| 	} | ||||
| 	sort.Strings(keys) | ||||
|  | ||||
| 	for _, key := range keys { | ||||
| 		value := obj[key] | ||||
| 		valueType := typeForValue(value, structName, tags, subStructMap, convertFloats) | ||||
|  | ||||
| 		//value = mergeElements(value) | ||||
|  | ||||
| 		//If a nested value, recurse | ||||
| 		switch value := value.(type) { | ||||
| 		case []interface{}: | ||||
| 			if len(value) > 0 { | ||||
| 				sub := "" | ||||
| 				if v, ok := value[0].(map[interface{}]interface{}); ok { | ||||
| 					sub = generateTypes(convertKeysToStrings(v), structName, tags, depth+1, subStructMap, convertFloats) + "}" | ||||
| 				} else if v, ok := value[0].(map[string]interface{}); ok { | ||||
| 					sub = generateTypes(v, structName, tags, depth+1, subStructMap, convertFloats) + "}" | ||||
| 				} | ||||
|  | ||||
| 				if sub != "" { | ||||
| 					subName := sub | ||||
|  | ||||
| 					if subStructMap != nil { | ||||
| 						if val, ok := subStructMap[sub]; ok { | ||||
| 							subName = val | ||||
| 						} else { | ||||
| 							subName = fmt.Sprintf("%v_sub%v", structName, len(subStructMap)+1) | ||||
|  | ||||
| 							subStructMap[sub] = subName | ||||
| 						} | ||||
| 					} | ||||
|  | ||||
| 					valueType = "[]" + subName | ||||
| 				} | ||||
| 			} | ||||
| 		case map[interface{}]interface{}: | ||||
| 			sub := generateTypes(convertKeysToStrings(value), structName, tags, depth+1, subStructMap, convertFloats) + "}" | ||||
| 			subName := sub | ||||
|  | ||||
| 			if subStructMap != nil { | ||||
| 				if val, ok := subStructMap[sub]; ok { | ||||
| 					subName = val | ||||
| 				} else { | ||||
| 					subName = fmt.Sprintf("%v_sub%v", structName, len(subStructMap)+1) | ||||
|  | ||||
| 					subStructMap[sub] = subName | ||||
| 				} | ||||
| 			} | ||||
| 			valueType = subName | ||||
| 		case map[string]interface{}: | ||||
| 			sub := generateTypes(value, structName, tags, depth+1, subStructMap, convertFloats) + "}" | ||||
| 			subName := sub | ||||
|  | ||||
| 			if subStructMap != nil { | ||||
| 				if val, ok := subStructMap[sub]; ok { | ||||
| 					subName = val | ||||
| 				} else { | ||||
| 					subName = fmt.Sprintf("%v_sub%v", structName, len(subStructMap)+1) | ||||
|  | ||||
| 					subStructMap[sub] = subName | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			valueType = subName | ||||
| 		} | ||||
|  | ||||
| 		fieldName := FmtFieldName(key) | ||||
|  | ||||
| 		tagList := make([]string, 0) | ||||
| 		for _, t := range tags { | ||||
| 			tagList = append(tagList, fmt.Sprintf("%s:\"%s\"", t, key)) | ||||
| 		} | ||||
|  | ||||
| 		structure += fmt.Sprintf("\n%s %s `%s`", | ||||
| 			fieldName, | ||||
| 			valueType, | ||||
| 			strings.Join(tagList, " ")) | ||||
| 	} | ||||
| 	return structure | ||||
| } | ||||
|  | ||||
| // FmtFieldName formats a string as a struct key | ||||
| // | ||||
| // Example: | ||||
| // | ||||
| //	FmtFieldName("foo_id") | ||||
| // | ||||
| // Output: FooID | ||||
| func FmtFieldName(s string) string { | ||||
| 	runes := []rune(s) | ||||
| 	for len(runes) > 0 && !unicode.IsLetter(runes[0]) && !unicode.IsDigit(runes[0]) { | ||||
| 		runes = runes[1:] | ||||
| 	} | ||||
| 	if len(runes) == 0 { | ||||
| 		return "_" | ||||
| 	} | ||||
|  | ||||
| 	s = stringifyFirstChar(string(runes)) | ||||
| 	name := lintFieldName(s) | ||||
| 	runes = []rune(name) | ||||
| 	for i, c := range runes { | ||||
| 		ok := unicode.IsLetter(c) || unicode.IsDigit(c) | ||||
| 		if i == 0 { | ||||
| 			ok = unicode.IsLetter(c) | ||||
| 		} | ||||
| 		if !ok { | ||||
| 			runes[i] = '_' | ||||
| 		} | ||||
| 	} | ||||
| 	s = string(runes) | ||||
| 	s = strings.Trim(s, "_") | ||||
| 	if len(s) == 0 { | ||||
| 		return "_" | ||||
| 	} | ||||
| 	return s | ||||
| } | ||||
|  | ||||
| func lintFieldName(name string) string { | ||||
| 	// Fast path for simple cases: "_" and all lowercase. | ||||
| 	if name == "_" { | ||||
| 		return name | ||||
| 	} | ||||
|  | ||||
| 	allLower := true | ||||
| 	for _, r := range name { | ||||
| 		if !unicode.IsLower(r) { | ||||
| 			allLower = false | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	if allLower { | ||||
| 		runes := []rune(name) | ||||
| 		if u := strings.ToUpper(name); commonInitialisms[u] { | ||||
| 			copy(runes[0:], []rune(u)) | ||||
| 		} else { | ||||
| 			runes[0] = unicode.ToUpper(runes[0]) | ||||
| 		} | ||||
| 		return string(runes) | ||||
| 	} | ||||
|  | ||||
| 	allUpperWithUnderscore := true | ||||
| 	for _, r := range name { | ||||
| 		if !unicode.IsUpper(r) && r != '_' { | ||||
| 			allUpperWithUnderscore = false | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	if allUpperWithUnderscore { | ||||
| 		name = strings.ToLower(name) | ||||
| 	} | ||||
|  | ||||
| 	// Split camelCase at any lower->upper transition, and split on underscores. | ||||
| 	// Check each word for common initialisms. | ||||
| 	runes := []rune(name) | ||||
| 	w, i := 0, 0 // index of start of word, scan | ||||
| 	for i+1 <= len(runes) { | ||||
| 		eow := false // whether we hit the end of a word | ||||
|  | ||||
| 		if i+1 == len(runes) { | ||||
| 			eow = true | ||||
| 		} else if runes[i+1] == '_' { | ||||
| 			// underscore; shift the remainder forward over any run of underscores | ||||
| 			eow = true | ||||
| 			n := 1 | ||||
| 			for i+n+1 < len(runes) && runes[i+n+1] == '_' { | ||||
| 				n++ | ||||
| 			} | ||||
|  | ||||
| 			// Leave at most one underscore if the underscore is between two digits | ||||
| 			if i+n+1 < len(runes) && unicode.IsDigit(runes[i]) && unicode.IsDigit(runes[i+n+1]) { | ||||
| 				n-- | ||||
| 			} | ||||
|  | ||||
| 			copy(runes[i+1:], runes[i+n+1:]) | ||||
| 			runes = runes[:len(runes)-n] | ||||
| 		} else if unicode.IsLower(runes[i]) && !unicode.IsLower(runes[i+1]) { | ||||
| 			// lower->non-lower | ||||
| 			eow = true | ||||
| 		} | ||||
| 		i++ | ||||
| 		if !eow { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		// [w,i) is a word. | ||||
| 		word := string(runes[w:i]) | ||||
| 		if u := strings.ToUpper(word); commonInitialisms[u] { | ||||
| 			// All the common initialisms are ASCII, | ||||
| 			// so we can replace the bytes exactly. | ||||
| 			copy(runes[w:], []rune(u)) | ||||
|  | ||||
| 		} else if strings.ToLower(word) == word { | ||||
| 			// already all lowercase, and not the first word, so uppercase the first character. | ||||
| 			runes[w] = unicode.ToUpper(runes[w]) | ||||
| 		} | ||||
| 		w = i | ||||
| 	} | ||||
| 	return string(runes) | ||||
| } | ||||
|  | ||||
| // generate an appropriate struct type entry | ||||
| func typeForValue(value interface{}, structName string, tags []string, subStructMap map[string]string, convertFloats bool) string { | ||||
| 	//Check if this is an array | ||||
| 	if objects, ok := value.([]interface{}); ok { | ||||
| 		types := make(map[reflect.Type]bool, 0) | ||||
| 		for _, o := range objects { | ||||
| 			types[reflect.TypeOf(o)] = true | ||||
| 		} | ||||
| 		if len(types) == 1 { | ||||
| 			return "[]" + typeForValue(mergeElements(objects).([]interface{})[0], structName, tags, subStructMap, convertFloats) | ||||
| 		} | ||||
| 		return "[]interface{}" | ||||
| 	} else if object, ok := value.(map[interface{}]interface{}); ok { | ||||
| 		return generateTypes(convertKeysToStrings(object), structName, tags, 0, subStructMap, convertFloats) + "}" | ||||
| 	} else if object, ok := value.(map[string]interface{}); ok { | ||||
| 		return generateTypes(object, structName, tags, 0, subStructMap, convertFloats) + "}" | ||||
| 	} else if reflect.TypeOf(value) == nil { | ||||
| 		return "interface{}" | ||||
| 	} | ||||
| 	v := reflect.TypeOf(value).Name() | ||||
| 	if v == "float64" && convertFloats { | ||||
| 		v = disambiguateFloatInt(value) | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
|  | ||||
| // All numbers will initially be read as float64 | ||||
| // If the number appears to be an integer value, use int instead | ||||
| func disambiguateFloatInt(value interface{}) string { | ||||
| 	const epsilon = .0001 | ||||
| 	vfloat := value.(float64) | ||||
| 	if !ForceFloats && math.Abs(vfloat-math.Floor(vfloat+epsilon)) < epsilon { | ||||
| 		var tmp int64 | ||||
| 		return reflect.TypeOf(tmp).Name() | ||||
| 	} | ||||
| 	return reflect.TypeOf(value).Name() | ||||
| } | ||||
|  | ||||
| // convert first character ints to strings | ||||
| func stringifyFirstChar(str string) string { | ||||
| 	first := str[:1] | ||||
|  | ||||
| 	i, err := strconv.ParseInt(first, 10, 8) | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return str | ||||
| 	} | ||||
|  | ||||
| 	return intToWordMap[i] + "_" + str[1:] | ||||
| } | ||||
|  | ||||
| func mergeElements(i interface{}) interface{} { | ||||
| 	switch i := i.(type) { | ||||
| 	default: | ||||
| 		return i | ||||
| 	case []interface{}: | ||||
| 		l := len(i) | ||||
| 		if l == 0 { | ||||
| 			return i | ||||
| 		} | ||||
| 		for j := 1; j < l; j++ { | ||||
| 			i[0] = mergeObjects(i[0], i[j]) | ||||
| 		} | ||||
| 		return i[0:1] | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func mergeObjects(o1, o2 interface{}) interface{} { | ||||
| 	if o1 == nil { | ||||
| 		return o2 | ||||
| 	} | ||||
|  | ||||
| 	if o2 == nil { | ||||
| 		return o1 | ||||
| 	} | ||||
|  | ||||
| 	if reflect.TypeOf(o1) != reflect.TypeOf(o2) { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	switch i := o1.(type) { | ||||
| 	default: | ||||
| 		return o1 | ||||
| 	case []interface{}: | ||||
| 		if i2, ok := o2.([]interface{}); ok { | ||||
| 			i3 := append(i, i2...) | ||||
| 			return mergeElements(i3) | ||||
| 		} | ||||
| 		return mergeElements(i) | ||||
| 	case map[string]interface{}: | ||||
| 		if i2, ok := o2.(map[string]interface{}); ok { | ||||
| 			for k, v := range i2 { | ||||
| 				if v2, ok := i[k]; ok { | ||||
| 					i[k] = mergeObjects(v2, v) | ||||
| 				} else { | ||||
| 					i[k] = v | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		return i | ||||
| 	case map[interface{}]interface{}: | ||||
| 		if i2, ok := o2.(map[interface{}]interface{}); ok { | ||||
| 			for k, v := range i2 { | ||||
| 				if v2, ok := i[k]; ok { | ||||
| 					i[k] = mergeObjects(v2, v) | ||||
| 				} else { | ||||
| 					i[k] = v | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		return i | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user