first commit

This commit is contained in:
what-00 2023-04-15 00:06:52 +08:00
commit 34bc26cb48
8 changed files with 708 additions and 0 deletions

1
README.md Normal file
View File

@ -0,0 +1 @@
#go-jsonpack

7
go.mod Normal file
View File

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

4
go.sum Normal file
View File

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

43
jsonpack.go Normal file
View File

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

110
jsonpackAst.go Normal file
View File

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

272
jsonpackDecode.go Normal file
View File

@ -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 解析错误, 未知符号")
// }

185
jsonpackEncode.go Normal file
View File

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

86
jsonpack_test.go Normal file
View File

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