186 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			186 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package jsonpack
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"reflect"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"github.com/samber/lo"
 | 
						|
)
 | 
						|
 | 
						|
type JsonEncode struct {
 | 
						|
	strings  []string
 | 
						|
	integers []string
 | 
						|
	floats   []string
 | 
						|
}
 | 
						|
 | 
						|
func (oJsonEncode *JsonEncode) astBuilder(item any) (AstToken, error) {
 | 
						|
	itemType := reflect.ValueOf(item).Kind()
 | 
						|
 | 
						|
	if item == nil {
 | 
						|
		return AstToken{category: "null", index: TOKEN_NULL}, nil
 | 
						|
	}
 | 
						|
 | 
						|
	if itemType == reflect.Slice {
 | 
						|
		tokens := []AstToken{}
 | 
						|
 | 
						|
		value := reflect.ValueOf(item)
 | 
						|
 | 
						|
		for i := 0; i < value.Len(); i++ {
 | 
						|
			// value.Index(i).Interface() 把 value 转换为 interface{} 类型
 | 
						|
			if resp, err := oJsonEncode.astBuilder(value.Index(i).Interface()); err == nil {
 | 
						|
				tokens = append(tokens, resp)
 | 
						|
			} else {
 | 
						|
				return AstToken{}, err
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		// return tokens, nil
 | 
						|
 | 
						|
		// Create a new sub-AST of type Array (@)
 | 
						|
		return AstToken{category: "@", child: tokens}, nil
 | 
						|
	}
 | 
						|
 | 
						|
	if itemType == reflect.Map {
 | 
						|
		// Create a new sub-AST of type Object ($)
 | 
						|
		// tokens := []any{AstToken{category: "$"}}
 | 
						|
		tokens := []AstToken{}
 | 
						|
 | 
						|
		iter := reflect.ValueOf(item).MapRange()
 | 
						|
 | 
						|
		for iter.Next() {
 | 
						|
			if resp, err := oJsonEncode.astBuilder(iter.Key().Interface()); err == nil {
 | 
						|
				tokens = append(tokens, resp)
 | 
						|
			} else {
 | 
						|
				return AstToken{}, err
 | 
						|
			}
 | 
						|
 | 
						|
			if resp, err := oJsonEncode.astBuilder(iter.Value().Interface()); err == nil {
 | 
						|
				tokens = append(tokens, resp)
 | 
						|
			} else {
 | 
						|
				return AstToken{}, err
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		// return tokens, nil
 | 
						|
		return AstToken{category: "$", child: tokens}, nil
 | 
						|
	}
 | 
						|
 | 
						|
	if item == "" {
 | 
						|
		return AstToken{category: "empty", index: TOKEN_EMPTY_STRING}, nil
 | 
						|
	}
 | 
						|
 | 
						|
	if itemType == reflect.String {
 | 
						|
		value := item.(string)
 | 
						|
		index := lo.IndexOf[string](oJsonEncode.strings, value)
 | 
						|
 | 
						|
		if index == -1 {
 | 
						|
			oJsonEncode.strings = append(oJsonEncode.strings, astEncode(value))
 | 
						|
			index = len(oJsonEncode.strings) - 1
 | 
						|
		}
 | 
						|
 | 
						|
		return AstToken{category: "strings", index: index}, nil
 | 
						|
	}
 | 
						|
 | 
						|
	switch itemType {
 | 
						|
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
 | 
						|
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | 
						|
 | 
						|
		value := astBase10to36(item.(int))
 | 
						|
		index := lo.IndexOf[string](oJsonEncode.integers, value)
 | 
						|
 | 
						|
		if index == -1 {
 | 
						|
			oJsonEncode.integers = append(oJsonEncode.integers, value)
 | 
						|
			index = len(oJsonEncode.integers) - 1
 | 
						|
		}
 | 
						|
 | 
						|
		return AstToken{category: "integers", index: index}, nil
 | 
						|
	}
 | 
						|
 | 
						|
	if itemType == reflect.Float32 || itemType == reflect.Float64 {
 | 
						|
		value := fmt.Sprintf("%v", item.(float64))
 | 
						|
		index := lo.IndexOf[string](oJsonEncode.floats, value)
 | 
						|
 | 
						|
		if index == -1 {
 | 
						|
			// 浮点数不用转36进制
 | 
						|
			oJsonEncode.floats = append(oJsonEncode.floats, value)
 | 
						|
			index = len(oJsonEncode.floats) - 1
 | 
						|
		}
 | 
						|
 | 
						|
		return AstToken{category: "floats", index: index}, nil
 | 
						|
	}
 | 
						|
 | 
						|
	if itemType == reflect.Bool {
 | 
						|
		if item.(bool) {
 | 
						|
			return AstToken{category: "boolean", index: TOKEN_TRUE}, nil
 | 
						|
		} else {
 | 
						|
			return AstToken{category: "boolean", index: TOKEN_FALSE}, nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return AstToken{}, errors.New(fmt.Sprintln("未知类型:", itemType, item))
 | 
						|
}
 | 
						|
 | 
						|
func (oJsonEncode *JsonEncode) astParser(token AstToken) (string, error) {
 | 
						|
 | 
						|
	if token.category == "@" || token.category == "$" {
 | 
						|
		var packed strings.Builder
 | 
						|
 | 
						|
		packed.WriteString(token.category)
 | 
						|
 | 
						|
		childPacked := []string{}
 | 
						|
 | 
						|
		for _, tk := range token.child {
 | 
						|
			if resp, err := oJsonEncode.astParser(tk); err == nil {
 | 
						|
				childPacked = append(childPacked, resp)
 | 
						|
			} else {
 | 
						|
				return "", err
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		packed.WriteString(strings.Join(childPacked, "|"))
 | 
						|
		packed.WriteString("]")
 | 
						|
 | 
						|
		return packed.String(), nil
 | 
						|
	}
 | 
						|
 | 
						|
	switch token.category {
 | 
						|
	case "strings":
 | 
						|
		return astBase10to36(token.index), nil
 | 
						|
	case "integers":
 | 
						|
		return astBase10to36(token.index + len(oJsonEncode.strings)), nil
 | 
						|
	case "floats":
 | 
						|
		return astBase10to36(token.index + len(oJsonEncode.strings) + len(oJsonEncode.integers)), nil
 | 
						|
	case "boolean":
 | 
						|
		return fmt.Sprintf("%v", token.index), nil
 | 
						|
	case "null":
 | 
						|
		return fmt.Sprintf("%v", TOKEN_NULL), nil
 | 
						|
	case "empty":
 | 
						|
		return fmt.Sprintf("%v", TOKEN_EMPTY_STRING), nil
 | 
						|
	}
 | 
						|
 | 
						|
	return "", errors.New(fmt.Sprintln("未知类型:", token.category))
 | 
						|
}
 | 
						|
 | 
						|
func (oJsonEncode *JsonEncode) toString(token AstToken) (string, error) {
 | 
						|
	str, err := oJsonEncode.astParser(token)
 | 
						|
	
 | 
						|
	if err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
 | 
						|
	var packed strings.Builder
 | 
						|
 | 
						|
	packed.WriteString(strings.Join(oJsonEncode.strings, "|"))
 | 
						|
	packed.WriteString("^")
 | 
						|
	packed.WriteString(strings.Join(oJsonEncode.integers, "|"))
 | 
						|
	packed.WriteString("^")
 | 
						|
	packed.WriteString(strings.Join(oJsonEncode.floats, "|"))
 | 
						|
	packed.WriteString("^")
 | 
						|
 | 
						|
	packed.WriteString(str)
 | 
						|
 | 
						|
	return packed.String(), nil
 | 
						|
}
 |