From 34bc26cb4839f43f49cc932be686c4e037a13926 Mon Sep 17 00:00:00 2001 From: what-00 Date: Sat, 15 Apr 2023 00:06:52 +0800 Subject: [PATCH] first commit --- README.md | 1 + go.mod | 7 ++ go.sum | 4 + jsonpack.go | 43 ++++++++ jsonpackAst.go | 110 +++++++++++++++++++ jsonpackDecode.go | 272 ++++++++++++++++++++++++++++++++++++++++++++++ jsonpackEncode.go | 185 +++++++++++++++++++++++++++++++ jsonpack_test.go | 86 +++++++++++++++ 8 files changed, 708 insertions(+) create mode 100644 README.md create mode 100644 go.mod create mode 100644 go.sum create mode 100644 jsonpack.go create mode 100644 jsonpackAst.go create mode 100644 jsonpackDecode.go create mode 100644 jsonpackEncode.go create mode 100644 jsonpack_test.go diff --git a/README.md b/README.md new file mode 100644 index 0000000..8b79f66 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +#go-jsonpack diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..3b47b9f --- /dev/null +++ b/go.mod @@ -0,0 +1,7 @@ +module git.fsdpf.net/go/jsonpack + +go 1.18 + +require github.com/samber/lo v1.38.1 + +require golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..7a26b7e --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= +github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= +golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= +golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= diff --git a/jsonpack.go b/jsonpack.go new file mode 100644 index 0000000..d11677c --- /dev/null +++ b/jsonpack.go @@ -0,0 +1,43 @@ +package jsonpack + +import ( + "errors" + "unicode/utf8" +) + +const ( + TOKEN_TRUE = -(iota + 1) + TOKEN_FALSE + TOKEN_NULL + TOKEN_EMPTY_STRING +) + +// 将对象加密为字符串 +func Pack(json any) (string, error) { + pack := &JsonEncode{} + + ast, err := pack.astBuilder(json) + + if err != nil { + return "", err + } + + return pack.toString(ast) +} + +// 解密数据到JSON字符串 +func Unpack(json string) (string, error) { + if !utf8.ValidString(json) { + return "", errors.New("jsonpack 无效的字符串格式, UTF8") + } + + unpack := &JsonDecode{} + + ast, err := unpack.astBuilder(json) + + if err != nil { + return "", err + } + + return unpack.toString(ast) +} diff --git a/jsonpackAst.go b/jsonpackAst.go new file mode 100644 index 0000000..8274e51 --- /dev/null +++ b/jsonpackAst.go @@ -0,0 +1,110 @@ +package jsonpack + +import ( + "math" + "regexp" + "strings" + + "github.com/samber/lo" +) + +type AstToken struct { + category string // array null, + index int + child []AstToken +} + +var baseCharacters = []rune("0123456789abcdefghijklmnopqrstuvwxyz") + +var baseCharactersMap = lo.Reduce[rune, map[rune]int](baseCharacters, func(carry map[rune]int, v rune, i int) map[rune]int { + carry[v] = i + return carry +}, make(map[rune]int)) + +var astEncodeReplace = map[string]string{ + " ": "+", + "+": "%2B", + "|": "%7C", + "^": "%5E", + "%": "%25", +} + +var astDecodeReplace = map[string]string{ + "+": " ", + "%2B": "+", + "%7C": "|", + "%5E": "^", + "%25": "%", +} + +// 字符串编码 +func astEncode(str string) string { + reg := regexp.MustCompile(`[\+ \|\^\%]`) + + return reg.ReplaceAllStringFunc(str, func(s string) string { + return astEncodeReplace[s] + }) +} + +// 字符串界面 +func astDecode(str string) string { + reg := regexp.MustCompile(`\+|%2B|%7C|%5E|%25`) + + return reg.ReplaceAllStringFunc(str, func(s string) string { + return astDecodeReplace[s] + }) +} + +// 10进制转36进制 +func astBase10to36(num int) string { + if num == 0 { + return "0" + } + + isSubFlag := num < 0 + + if isSubFlag { + num = num * -1 + } + + str := "" + + for num != 0 { + p := num % 36 + str = string(baseCharacters[p]) + str + num = num / 36 + } + + if isSubFlag { + return "-" + str + } + + return str +} + +// 36进制转10进制 +func astBase36to10(str string) int { + str = strings.ToLower(str) + + isSubFlag := str[0:1] == "-" + + if isSubFlag { + str = str[1:] + } + + v := 0.0 + + length := len(str) + + for i := 0; i < length; i++ { + s := rune(str[i]) + index := baseCharactersMap[s] + v += float64(index) * math.Pow(float64(36), float64(length-1-i)) // 倒序 + } + + if isSubFlag { + return -int(v) + } + + return int(v) +} diff --git a/jsonpackDecode.go b/jsonpackDecode.go new file mode 100644 index 0000000..7d38de6 --- /dev/null +++ b/jsonpackDecode.go @@ -0,0 +1,272 @@ +package jsonpack + +import ( + "errors" + "fmt" + "reflect" + "strconv" + "strings" +) + +type JsonDecode struct { + dictionaries []any + tokenIndex int +} + +func (oJsonDecode *JsonDecode) astBuilder(packed string) (string, error) { + aStr := strings.Split(packed, "^") + + if len(aStr) != 4 { + return "", errors.New("jsonpack 解析错误") + } + + if aStr[0] != "" { + for _, v := range strings.Split(aStr[0], "|") { + oJsonDecode.dictionaries = append(oJsonDecode.dictionaries, astDecode(v)) + } + } + + if aStr[1] != "" { + for _, v := range strings.Split(aStr[1], "|") { + oJsonDecode.dictionaries = append(oJsonDecode.dictionaries, astBase36to10(v)) + } + } + + if aStr[2] != "" { + for _, v := range strings.Split(aStr[2], "|") { + if value, err := strconv.ParseFloat(v, 64); err == nil { + oJsonDecode.dictionaries = append(oJsonDecode.dictionaries, value) + } else { + return "", fmt.Errorf("jsonpack 浮点数转换失败 %#v", v) + } + } + } + + return aStr[3], nil +} + +func (oJsonDecode *JsonDecode) astParser(tokens *[]rune) (string, error) { + len := len(*tokens) + + symbol := (*tokens)[oJsonDecode.tokenIndex] + + oJsonDecode.tokenIndex = oJsonDecode.tokenIndex + 1 + + if symbol == '@' { + node := []string{} + + for ; oJsonDecode.tokenIndex < len; oJsonDecode.tokenIndex++ { + value := rune((*tokens)[oJsonDecode.tokenIndex]) + + if value == ']' { + return "[" + strings.Join(node, ",") + "]", nil + } + + if value == '@' || value == '$' { + // 递归解析 + if res, err := oJsonDecode.astParser(tokens); err == nil { + node = append(node, res) + } else { + return "", errors.New("jsonpack 递归解析错误") + } + } else { + // 解析成员 + if res, err := oJsonDecode.astParserValue(value); err == nil { + node = append(node, res) + } else { + return "", errors.New("jsonpack 解析成员错误") + } + } + } + + return "[" + strings.Join(node, ",") + "]", nil + } + + if symbol == '$' { + node := []string{} + for ; oJsonDecode.tokenIndex < len; oJsonDecode.tokenIndex++ { + var key string + + kIndex := rune((*tokens)[oJsonDecode.tokenIndex]) + + if kIndex == ']' { + return "{" + strings.Join(node, ",") + "}", nil + } + + if kIndex == TOKEN_EMPTY_STRING { + key = "" + } else { + key = strconv.Quote(oJsonDecode.dictionaries[kIndex].(string)) + } + + oJsonDecode.tokenIndex++ + + value := rune((*tokens)[oJsonDecode.tokenIndex]) + + if value == '@' || value == '$' { + // 递归解析 + if res, err := oJsonDecode.astParser(tokens); err == nil { + node = append(node, key+":"+res) + } else { + return "", err + } + } else { + // 解析成员 + if res, err := oJsonDecode.astParserValue(value); err == nil { + node = append(node, key+":"+res) + } else { + return "", err + } + } + } + + return "{" + strings.Join(node, ",") + "}", nil + } + + return "", errors.New("jsonpack 解析错误, 未知符号") +} + +func (oJsonDecode *JsonDecode) astParserValue(value rune) (string, error) { + switch value { + case TOKEN_TRUE: + return "true", nil + case TOKEN_FALSE: + return "false", nil + case TOKEN_NULL: + return "null", nil + case TOKEN_EMPTY_STRING: + return `""`, nil + default: + item := oJsonDecode.dictionaries[value] + + switch reflect.ValueOf(item).Kind() { + case reflect.String: + return strconv.Quote(item.(string)), nil + case reflect.Int, reflect.Float64: + return fmt.Sprintf("%v", item), nil + } + } + + return "", errors.New("jsonpack 未知数据类型") +} + +func (oJsonDecode *JsonDecode) toString(token string) (string, error) { + var number36 strings.Builder + + var tokens []rune + + len := len(token) + + for i := 0; i < len; i++ { + symbol := rune(token[i]) + + if symbol == '|' || symbol == '$' || symbol == '@' || symbol == ']' { + if number36.Len() > 0 { + tokens = append(tokens, rune(astBase36to10(number36.String()))) + + number36.Reset() + } + + if symbol != '|' { + tokens = append(tokens, symbol) + } + + } else { + number36.WriteRune(symbol) + } + } + + return oJsonDecode.astParser(&tokens) +} + +// func (oJsonDecode *JsonDecode) astParser(tokens *[]rune) (any, error) { +// len := len(*tokens) + +// symbol := (*tokens)[oJsonDecode.tokenIndex] + +// oJsonDecode.tokenIndex = oJsonDecode.tokenIndex + 1 + +// if symbol == '@' { +// node := []any{} + +// for ; oJsonDecode.tokenIndex < len; oJsonDecode.tokenIndex++ { +// value := rune((*tokens)[oJsonDecode.tokenIndex]) + +// if value == ']' { +// return node, nil +// } + +// if value == '@' || value == '$' { +// if resp, err := oJsonDecode.astParser(tokens); err == nil { +// node = append(node, resp) +// } else { +// return nil, errors.New("jsonpack 解析失败") +// } +// } else { +// switch value { +// case TOKEN_TRUE: +// node = append(node, true) +// case TOKEN_FALSE: +// node = append(node, false) +// case TOKEN_NULL: +// node = append(node, nil) +// case TOKEN_EMPTY_STRING: +// node = append(node, "") +// default: +// node = append(node, oJsonDecode.dictionaries[value]) +// } +// } +// } + +// return node, nil +// } + +// if symbol == '$' { +// node := map[string]any{} + +// for ; oJsonDecode.tokenIndex < len; oJsonDecode.tokenIndex++ { +// var key string + +// kIndex := rune((*tokens)[oJsonDecode.tokenIndex]) + +// if kIndex == ']' { +// return node, nil +// } + +// if kIndex == TOKEN_EMPTY_STRING { +// key = "" +// } else { +// key = oJsonDecode.dictionaries[kIndex].(string) +// } + +// oJsonDecode.tokenIndex++ + +// value := rune((*tokens)[oJsonDecode.tokenIndex]) + +// if value == '@' || value == '$' { +// if res, err := oJsonDecode.astParser(tokens); err == nil { +// node[key] = res +// } else { +// return nil, errors.New("jsonpack 递归解析 数组或对象 错误") +// } +// } else { +// switch value { +// case TOKEN_TRUE: +// node[key] = true +// case TOKEN_FALSE: +// node[key] = false +// case TOKEN_NULL: +// node[key] = nil +// case TOKEN_EMPTY_STRING: +// node[key] = "" +// default: +// node[key] = oJsonDecode.dictionaries[value] +// } +// } +// } + +// return node, nil +// } + +// return nil, errors.New("jsonpack 解析错误, 未知符号") +// } diff --git a/jsonpackEncode.go b/jsonpackEncode.go new file mode 100644 index 0000000..afd9c73 --- /dev/null +++ b/jsonpackEncode.go @@ -0,0 +1,185 @@ +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 +} diff --git a/jsonpack_test.go b/jsonpack_test.go new file mode 100644 index 0000000..52fd2a3 --- /dev/null +++ b/jsonpack_test.go @@ -0,0 +1,86 @@ +package jsonpack + +import ( + "fmt" + "testing" +) + +func TestAstEncode(t *testing.T) { + a := "1+2|3^4%5" + b := "1%2B2%7C3%5E4%255" + + b1 := astEncode(a) + + if b == b1 { + t.Logf("输入=>%s, 输出=>%s", a, b) + } else { + t.Fatalf("输入=>%s, 预计输出=>%s, 实际输出=>%s", a, b, b1) + } + t.Log("done") +} + +func TestAstDecode(t *testing.T) { + a := "1%2B2%7C3%5E4%255" + b := "1+2|3^4%5" + + b1 := astDecode(a) + + if b == b1 { + t.Logf("输入=>%s, 输出=>%s", a, b) + } else { + t.Fatalf("输入=>%s, 预计输出=>%s, 实际输出=>%s", a, b, b1) + } + t.Log("done") +} + +func TestAstBase10to36(t *testing.T) { + a := 65036 + b := "1e6k" + b1 := astBase10to36(a) + + if b == b1 { + t.Logf("输入(10)=>%d, 输出(36)=>%s", a, b) + } else { + t.Fatalf("输入(10)=>%d, 输出(36)=>%s", a, b1) + } + t.Log("done") +} + +func TestAstBase36to10(t *testing.T) { + a := "1e6k" + b := 65036 + b1 := astBase36to10(a) + + if b == b1 { + t.Logf("输入(36)=>%s, 输出(10)=>%d", a, b) + } else { + t.Fatalf("输入(36)=>%s, 输出(10)=>%d", a, b1) + } + t.Log("done") +} + +func TestPack(t *testing.T) { + a := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"} + a1 := []any{"France2", "France1", "1", nil, 123, 123.2222, -15555, -1.2, false, true, a} + + b := "France2|France1|1|France|Paris|Italy|Rome|Japan|Tokyo|India|New+delhi^3f|-c03^123.2222|-1.2^@0|1|2|b|d|c|e|-2|-1|$3|4|5|6|7|8|9|a]]" + + b1, _ := Pack(a1) + + if b == b1 { + t.Logf("\n 输入=>%s \n 输出=>%s", a1, b) + } else { + t.Fatalf("\n 输入=>%s \n 预计输出=>%s \n 实际输出=>%s", a1, b, b1) + } + + t.Log("done") +} + +func TestUnpack(t *testing.T) { + a := "Fran\"ce2|Franc什么e1|1|France|Pa:ris|Italy|Rome|Japan|Tokyo|India|New+delhi^3f|-c03^123.2222|-1.2^@0|1|2|b|d|c|e|-2|-1|$3|4|5|6|7|8|9|a]]" + + b, _ := Unpack(a) + + // fmt.Sprintf("类型: %T, 内容: %+v", b, b) + t.Logf(b) +}