|
|
- package json
-
- import (
- "encoding/json"
- "errors"
-
- lua "github.com/yuin/gopher-lua"
- )
-
- var (
- errNested = errors.New("cannot encode recursively nested tables to JSON")
- errSparseArray = errors.New("cannot encode sparse array")
- errInvalidKeys = errors.New("cannot encode mixed or invalid key types")
- )
-
- type invalidTypeError lua.LValueType
-
- func (i invalidTypeError) Error() string {
- return `cannot encode ` + lua.LValueType(i).String() + ` to JSON`
- }
-
- type jsonValue struct {
- lua.LValue
- visited map[*lua.LTable]bool
- }
-
- func (j jsonValue) MarshalJSON() (data []byte, err error) {
- switch converted := j.LValue.(type) {
- case lua.LBool:
- data, err = json.Marshal(bool(converted))
- case lua.LNumber:
- data, err = json.Marshal(float64(converted))
- case *lua.LNilType:
- data = []byte(`null`)
- case lua.LString:
- data, err = json.Marshal(string(converted))
- case *lua.LTable:
- if j.visited[converted] {
- return nil, errNested
- }
- j.visited[converted] = true
-
- key, value := converted.Next(lua.LNil)
-
- switch key.Type() {
- case lua.LTNil: // empty table
- data = []byte(`[]`)
- case lua.LTNumber:
- arr := make([]jsonValue, 0, converted.Len())
- expectedKey := lua.LNumber(1)
- for key != lua.LNil {
- if key.Type() != lua.LTNumber {
- err = errInvalidKeys
- return
- }
- if expectedKey != key {
- err = errSparseArray
- return
- }
- arr = append(arr, jsonValue{value, j.visited})
- expectedKey++
- key, value = converted.Next(key)
- }
- data, err = json.Marshal(arr)
- case lua.LTString:
- obj := make(map[string]jsonValue)
- for key != lua.LNil {
- if key.Type() != lua.LTString {
- err = errInvalidKeys
- return
- }
- obj[key.String()] = jsonValue{value, j.visited}
- key, value = converted.Next(key)
- }
- data, err = json.Marshal(obj)
- default:
- err = errInvalidKeys
- }
- default:
- err = invalidTypeError(j.LValue.Type())
- }
- return
- }
-
- // ValueDecode converts the JSON encoded data to Lua values.
- func ValueDecode(L *lua.LState, data []byte) (lua.LValue, error) {
- var value interface{}
- err := json.Unmarshal(data, &value)
- if err != nil {
- return nil, err
- }
- return decode(L, value), nil
- }
-
- func decode(L *lua.LState, value interface{}) lua.LValue {
- switch converted := value.(type) {
- case bool:
- return lua.LBool(converted)
- case float64:
- return lua.LNumber(converted)
- case string:
- return lua.LString(converted)
- case []interface{}:
- arr := L.CreateTable(len(converted), 0)
- for _, item := range converted {
- arr.Append(decode(L, item))
- }
- return arr
- case map[string]interface{}:
- tbl := L.CreateTable(0, len(converted))
- for key, item := range converted {
- tbl.RawSetH(lua.LString(key), decode(L, item))
- }
- return tbl
- case nil:
- return lua.LNil
- }
- panic("unreachable")
- }
-
- // ValueEncode returns the JSON encoding of value.
- func ValueEncode(value lua.LValue) ([]byte, error) {
- return json.Marshal(jsonValue{
- LValue: value,
- visited: make(map[*lua.LTable]bool),
- })
- }
|