本代码片段根源是网上的序列化包,是保存到本地之后,进行的更改,其中引入了【decimal】包来解决反序列化数字精度问题,代码如下:
package utils
import (
"bytes"
"fmt"
"reflect"
"sort"
"strconv"
"strings"
"github.com/shopspring/decimal"
)
const UNSERIALIZABLE_OBJECT_MAX_LEN = int64(10 * 1024 * 1024 * 1024)
func Marshal(value interface{}) ([]byte, error) {
if value == nil {
return MarshalNil(), nil
}
t := reflect.TypeOf(value)
switch t.Kind() {
case reflect.Bool:
return MarshalBool(value.(bool)), nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64:
return MarshalNumber(value), nil
case reflect.String:
return MarshalString(value.(string)), nil
case reflect.Map:
return MarshalMap(value)
case reflect.Slice:
return MarshalSlice(value)
case reflect.Struct:
return []byte("d:" + value.(decimal.Decimal).String() + ";"), nil
default:
return nil, fmt.Errorf("Marshal: Unknown type %T with value %#v", t, value)
}
}
func MarshalNil() []byte {
return []byte("N;")
}
func MarshalBool(value bool) []byte {
if value {
return []byte("b:1;")
}
return []byte("b:0;")
}
func MarshalNumber(value interface{}) []byte {
var val string
isFloat := false
switch value.(type) {
default:
val = "0"
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
val, _ = NumericalToString(value)
case float32, float64:
val, _ = NumericalToString(value)
isFloat = true
}
if isFloat {
return []byte("d:" + val + ";")
} else {
return []byte("i:" + val + ";")
}
}
func MarshalString(value string) []byte {
return []byte(fmt.Sprintf("s:%d:\"%s\";", len(value), value))
}
func MarshalMap(value interface{}) ([]byte, error) {
s := reflect.ValueOf(value)
mapKeys := s.MapKeys()
sort.Slice(mapKeys, func(i, j int) bool {
return LessValue(mapKeys[i], mapKeys[j])
})
var buffer bytes.Buffer
for _, mapKey := range mapKeys {
m, err := Marshal(mapKey.Interface())
if err != nil {
return nil, err
}
buffer.Write(m)
m, err = Marshal(s.MapIndex(mapKey).Interface())
if err != nil {
return nil, err
}
buffer.Write(m)
}
return []byte(fmt.Sprintf("a:%d:{%s}", s.Len(), buffer.String())), nil
}
func MarshalSlice(value interface{}) ([]byte, error) {
s := reflect.ValueOf(value)
var buffer bytes.Buffer
for i := 0; i < s.Len(); i++ {
m, err := Marshal(i)
if err != nil {
return nil, err
}
buffer.Write(m)
m, err = Marshal(s.Index(i).Interface())
if err != nil {
return nil, err
}
buffer.Write(m)
}
return []byte(fmt.Sprintf("a:%d:{%s}", s.Len(), buffer.String())), nil
}
// numericalValue returns the float64 representation of a value if it is a
// numerical type - integer, unsigned integer or float. If the value is not a
// numerical type then the second argument is false and the value returned
// should be disregarded.
func NumericalValue(value reflect.Value) (float64, bool) {
switch value.Type().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return float64(value.Int()), true
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return float64(value.Uint()), true
case reflect.Float32, reflect.Float64:
return value.Float(), true
default:
return 0, false
}
}
// lessValue compares two reflect.Value instances and returns true if a is
// considered to be less than b.
//
// This function is used to sort keys for what amounts to associate arrays and
// objects in PHP. These are represented as slices and maps in Go. Since Go
// randomised map iterators we need to make sure we always return the keys of an
// associative array or object in a predicable order.
//
// The keys can be numerical, strings or a combination of both. We treat numbers
// (integers, unsigned integers and floats) as always less than strings. Numbers
// are ordered by magnitude (ignoring types) and strings are orders
// lexicographically.
//
// If keys are of any other type the behavior of the comparison is undefined. If
// there is a legitimate reason why keys could be other types then this function
// should be updated accordingly.
func LessValue(a, b reflect.Value) bool {
aValue, aNumerical := NumericalValue(a)
bValue, bNumerical := NumericalValue(b)
if aNumerical && bNumerical {
return aValue < bValue
}
if !aNumerical && !bNumerical {
// In theory this should mean they are both strings. In reality
// they could be any other type and the String() representation
// will be something like "<bool>" if it is not a string. Since
// distinct values of non-strings still return the same value
// here that's what makes the ordering undefined.
return strings.Compare(a.String(), b.String()) < 0
}
// Numerical values are always treated as less than other types
// (including strings that might represent numbers themselves). The
// inverse is also true.
return aNumerical && !bNumerical
}
func LowerCaseFirstLetter(s string) string {
return strings.ToLower(s[0:1]) + s[1:]
}
func NumericalToString(value interface{}) (string, bool) {
var val string
switch value.(type) {
default:
return "0", false
case int:
intVal, _ := value.(int)
val = strconv.FormatInt(int64(intVal), 10)
case int8:
intVal, _ := value.(int8)
val = strconv.FormatInt(int64(intVal), 10)
case int16:
intVal, _ := value.(int16)
val = strconv.FormatInt(int64(intVal), 10)
case int32:
intVal, _ := value.(int32)
val = strconv.FormatInt(int64(intVal), 10)
case int64:
intVal, _ := value.(int64)
val = strconv.FormatInt(int64(intVal), 10)
case uint:
intVal, _ := value.(uint)
val = strconv.FormatUint(uint64(intVal), 10)
case uint8:
intVal, _ := value.(uint8)
val = strconv.FormatUint(uint64(intVal), 10)
case uint16:
intVal, _ := value.(uint16)
val = strconv.FormatUint(uint64(intVal), 10)
case uint32:
intVal, _ := value.(uint32)
val = strconv.FormatUint(uint64(intVal), 10)
case uint64:
intVal, _ := value.(uint64)
val = strconv.FormatUint(uint64(intVal), 10)
case float32:
floatVal, _ := value.(float32)
val = strconv.FormatFloat(float64(floatVal), 'f', -1, 32)
case float64:
floatVal, _ := value.(float64)
val = strconv.FormatFloat(float64(floatVal), 'f', -1, 64)
}
return val, true
}
func UnMarshal(data []byte) (interface{}, error) {
reader := bytes.NewReader(data)
return unMarshalByReader(reader)
}
func unMarshalByReader(reader *bytes.Reader) (interface{}, error) {
for {
if token, _, err := reader.ReadRune(); err == nil {
switch token {
default:
return nil, fmt.Errorf("UnMarshal: Unknown token %#U", token)
case 'N':
return unMarshalNil(reader)
case 'b':
return unMarshalBool(reader)
case 'i':
return unMarshalNumber(reader, false)
case 'd':
return unMarshalNumber(reader, true)
case 's':
return unMarshalString(reader, true)
case 'a':
return unMarshalArray(reader)
// case 'O':
// case 'C':
// case 'R', 'r':
// case 'x':
}
}
return nil, nil
}
}
func unMarshalNil(reader *bytes.Reader) (interface{}, error) {
expect(reader, ';')
return nil, nil
}
func unMarshalBool(reader *bytes.Reader) (interface{}, error) {
var (
raw rune
err error
)
err = expect(reader, ':')
if err != nil {
return nil, err
}
if raw, _, err = reader.ReadRune(); err != nil {
return nil, fmt.Errorf("UnMarshal: Error while reading bool value: %v", err)
}
err = expect(reader, ';')
if err != nil {
return nil, err
}
return raw == '1', nil
}
func unMarshalNumber(reader *bytes.Reader, isFloat bool) (interface{}, error) {
var (
raw string
err error
val interface{}
)
err = expect(reader, ':')
if err != nil {
return nil, err
}
if raw, err = readUntil(reader, ';'); err != nil {
return nil, fmt.Errorf("UnMarshal: Error while reading number value: %v", err)
} else {
if isFloat {
if s, err := decimal.NewFromString(raw); err != nil {
return nil, fmt.Errorf("UnMarshal: Unable to convert %s to float: %v", raw, err)
} else {
val = s.Round(100)
}
} else {
if val, err = strconv.Atoi(raw); err != nil {
return nil, fmt.Errorf("UnMarshal: Unable to convert %s to int: %v", raw, err)
}
}
}
return val, nil
}
func unMarshalString(reader *bytes.Reader, isFinal bool) (string, error) {
var (
err error
val string
strLen int
readLen int
)
strLen, err = readLength(reader)
err = expect(reader, '"')
if err != nil {
return val, err
}
if strLen > 0 {
buf := make([]byte, strLen, strLen)
if readLen, err = reader.Read(buf); err != nil {
return val, fmt.Errorf("UnMarshal: Error while reading string value: %v", err)
} else {
if readLen != strLen {
return val, fmt.Errorf("UnMarshal: Unable to read string. Expected %d but have got %d bytes", strLen, readLen)
} else {
val = string(buf)
}
}
}
err = expect(reader, '"')
if err != nil {
return val, err
}
if isFinal {
err = expect(reader, ';')
if err != nil {
return val, err
}
}
return val, nil
}
func unMarshalArray(reader *bytes.Reader) (interface{}, error) {
var arrLen int
var err error
val := make(map[string]interface{})
arrLen, err = readLength(reader)
if err != nil {
return nil, err
}
err = expect(reader, '{')
if err != nil {
return nil, err
}
indexLen := 0
for i := 0; i < arrLen; i++ {
k, err := unMarshalByReader(reader)
if err != nil {
return nil, err
}
v, err := unMarshalByReader(reader)
if err != nil {
return nil, err
}
// if errKey == nil && errVal == nil {
// val[k] = v
switch t := k.(type) {
default:
return nil, fmt.Errorf("UnMarshal: Unexpected key type %T", t)
case string:
stringKey, _ := k.(string)
val[stringKey] = v
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:
// intKey, _ := k.(int)
// val[strconv.Itoa(intKey)] = v
stringKey, _ := NumericalToString(k)
val[stringKey] = v
if i == k {
indexLen++
}
}
// } else {
// return nil, fmt.Errorf("UnMarshal: Error while reading key or(and) value of array")
// }
}
err = expect(reader, '}')
if err != nil {
return nil, err
}
if indexLen == arrLen {
var slice []interface{}
for _, row := range val {
slice = append(slice, row)
}
return slice, nil
}
return val, nil
}
func expect(reader *bytes.Reader, expected rune) error {
if token, _, err := reader.ReadRune(); err != nil {
return fmt.Errorf("UnMarshal: Error while reading expected rune %#U: %v", expected, err)
} else if token != expected {
return fmt.Errorf("UnMarshal: Expected %#U but have got %#U", expected, token)
}
return nil
}
func readUntil(reader *bytes.Reader, stop rune) (string, error) {
var (
token rune
err error
)
buf := bytes.NewBuffer([]byte{})
for {
if token, _, err = reader.ReadRune(); err != nil || token == stop {
break
} else {
buf.WriteRune(token)
}
}
return buf.String(), err
}
func readLength(reader *bytes.Reader) (int, error) {
var (
raw string
err error
val int
)
err = expect(reader, ':')
if err != nil {
return 0, err
}
if raw, err = readUntil(reader, ':'); err != nil {
return 0, fmt.Errorf("UnMarshal: Error while reading lenght of value: %v", err)
} else {
if val, err = strconv.Atoi(raw); err != nil {
return 0, fmt.Errorf("UnMarshal: Unable to convert %s to int: %v", raw, err)
} else if int64(val) > UNSERIALIZABLE_OBJECT_MAX_LEN {
return 0, fmt.Errorf("UnMarshal: Unserializable object length looks too big(%d). If you are sure you wanna unserialise it, please increase UNSERIALIZABLE_OBJECT_MAX_LEN const", val)
val = 0
}
}
return val, nil
}
工程目录结构如下: