From df05deae0e5b2ee49bf4109b2163f1c38615c281 Mon Sep 17 00:00:00 2001 From: Harry Bagdi Date: Fri, 3 Dec 2021 09:14:30 -0800 Subject: [PATCH 1/8] add go.mod --- go.mod | 5 +++++ go.sum | 6 ++++++ 2 files changed, 11 insertions(+) create mode 100644 go.mod create mode 100644 go.sum diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..52f7d98 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module layeh.com/gopher-json + +go 1.17 + +require github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e69c257 --- /dev/null +++ b/go.sum @@ -0,0 +1,6 @@ +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw= +github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA= +golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= From 6654e68f038e6e1e48da1099add92733f791e8bf Mon Sep 17 00:00:00 2001 From: Harry Bagdi Date: Fri, 3 Dec 2021 09:15:03 -0800 Subject: [PATCH 2/8] marshal userdata as json 'null' --- json.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/json.go b/json.go index c34af2d..c8a8148 100644 --- a/json.go +++ b/json.go @@ -4,7 +4,7 @@ import ( "encoding/json" "errors" - "github.com/yuin/gopher-lua" + lua "github.com/yuin/gopher-lua" ) // Preload adds json to the given Lua state's package.preload table. After it @@ -131,6 +131,9 @@ func (j jsonValue) MarshalJSON() (data []byte, err error) { default: err = errInvalidKeys } + case *lua.LUserData: + data, err = json.Marshal(nil) + return default: err = invalidTypeError(j.LValue.Type()) } From 04cc2cd397135724665b0d581d3564bb0d2c994f Mon Sep 17 00:00:00 2001 From: Harry Bagdi Date: Fri, 3 Dec 2021 09:18:40 -0800 Subject: [PATCH 3/8] fix the go module name --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 52f7d98..79fe6f7 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module layeh.com/gopher-json +module github.com/layeh/gopher-json go 1.17 From 3030ea88774dff58f8b589f6fd498279b5fb3b06 Mon Sep 17 00:00:00 2001 From: Fero <6863207+mikefero@users.noreply.github.com> Date: Fri, 25 Mar 2022 12:52:50 -0400 Subject: [PATCH 4/8] fix: handle sparse array for entity check error messages (#1) Certain Kong error message are in the following format which indicate index value for error in an array: ``` errors = { config = { response_if_status_code = { [2] = "value should be between 100 and 599" } } } ``` This fix allows for sparse array and converts the key (which is the index in this case) into part of the error message. * test: add a test for UserData removal --- json.go | 9 ++++--- json_test.go | 72 +++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 73 insertions(+), 8 deletions(-) diff --git a/json.go b/json.go index c8a8148..0ee7117 100644 --- a/json.go +++ b/json.go @@ -3,6 +3,7 @@ package json import ( "encoding/json" "errors" + "fmt" lua "github.com/yuin/gopher-lua" ) @@ -56,7 +57,6 @@ func apiEncode(L *lua.LState) int { 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") ) @@ -109,10 +109,11 @@ func (j jsonValue) MarshalJSON() (data []byte, err error) { return } if expectedKey != key { - err = errSparseArray - return + errValue := lua.LString(fmt.Sprintf("[%s] = %s", key.String(), value.String())) + arr = append(arr, jsonValue{errValue, j.visited}) + } else { + arr = append(arr, jsonValue{value, j.visited}) } - arr = append(arr, jsonValue{value, j.visited}) expectedKey++ key, value = converted.Next(key) } diff --git a/json_test.go b/json_test.go index fbd627d..093810a 100644 --- a/json_test.go +++ b/json_test.go @@ -2,9 +2,10 @@ package json import ( "encoding/json" + "fmt" "testing" - "github.com/yuin/gopher-lua" + lua "github.com/yuin/gopher-lua" ) func TestSimple(t *testing.T) { @@ -21,9 +22,6 @@ func TestSimple(t *testing.T) { assert(json.encode({}) == "[]") assert(json.encode({1, 2, 3}) == "[1,2,3]") - local _, err = json.encode({1, 2, [10] = 3}) - assert(string.find(err, "sparse array")) - local _, err = json.encode({1, 2, 3, name = "Tim"}) assert(string.find(err, "mixed or invalid key types")) @@ -62,6 +60,16 @@ func TestSimple(t *testing.T) { a[i] = i end assert(json.encode(a) == "[1,2,3,4,5]") + + -- UserData removal + local t = setmetatable({10}, { + __call = function(t, value) + return value + end + }) + + assert(t(37) == 37) + assert(json.encode(t) == "[10]") ` s := lua.NewState() defer s.Close() @@ -97,3 +105,59 @@ func TestDecodeValue_jsonNumber(t *testing.T) { t.Fatalf("expecting LString, got %T", v) } } + +func TestEncode_SparseArray(t *testing.T) { + tests := []struct { + table string + expected string + }{ + { + table: `{ + 1, + 2, + [10] = 3 + }`, + expected: `[1,2,"[10] = 3"]`, + }, + { + table: `{ + nested = { + [37] = "index 37" + } + }`, + expected: `{"nested":["[37] = index 37"]}`, + }, + { + table: `{ + nested = { + "index 1", + [37] = "index 37" + } + }`, + expected: `{"nested":["index 1","[37] = index 37"]}`, + }, + { + table: `{ + nested = { + "index 1", + [37] = "index 37" + } + }`, + expected: `{"nested":["index 1","[37] = index 37"]}`, + }, + } + + for _, test := range tests { + s := lua.NewState() + defer s.Close() + Preload(s) + + luaScript := fmt.Sprintf(` + local json = require("json") + local t = %s + assert(json.encode(t) == '%s')`, test.table, test.expected) + if err := s.DoString(luaScript); err != nil { + t.Error(err) + } + } +} From fe2f8bcf1867444a4ec24ca0b0c50eaad97c1a97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20L=C3=BCer?= Date: Wed, 20 Sep 2023 11:23:33 +0200 Subject: [PATCH 5/8] fix: go.mod module name --- .gitignore | 1 + go.mod | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..485dee6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea diff --git a/go.mod b/go.mod index 79fe6f7..d669b1e 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/layeh/gopher-json +module github.com/HannesLueer/gopher-json go 1.17 From 8847ff5293918c714d0559cde702d0d752ee1b55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20L=C3=BCer?= Date: Wed, 20 Sep 2023 14:21:58 +0200 Subject: [PATCH 6/8] feat: lua sparse array to json map --- go.mod | 2 +- json.go | 20 +++++++++++++------- json_test.go | 41 +++++++++++++++++++++++++++++++---------- 3 files changed, 45 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index d669b1e..fa60e0a 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,5 @@ module github.com/HannesLueer/gopher-json -go 1.17 +go 1.21 require github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 diff --git a/json.go b/json.go index 0ee7117..f8c84c3 100644 --- a/json.go +++ b/json.go @@ -3,15 +3,13 @@ package json import ( "encoding/json" "errors" - "fmt" - lua "github.com/yuin/gopher-lua" ) // Preload adds json to the given Lua state's package.preload table. After it // has been preloaded, it can be loaded using require: // -// local json = require("json") +// local json = require("json") func Preload(L *lua.LState) { L.PreloadModule("json", Loader) } @@ -102,22 +100,30 @@ func (j jsonValue) MarshalJSON() (data []byte, err error) { data = []byte(`[]`) case lua.LTNumber: arr := make([]jsonValue, 0, converted.Len()) + m := make(map[string]jsonValue) expectedKey := lua.LNumber(1) + isSparseArray := false for key != lua.LNil { if key.Type() != lua.LTNumber { err = errInvalidKeys return } if expectedKey != key { - errValue := lua.LString(fmt.Sprintf("[%s] = %s", key.String(), value.String())) - arr = append(arr, jsonValue{errValue, j.visited}) - } else { + isSparseArray = true + } + if !isSparseArray { arr = append(arr, jsonValue{value, j.visited}) } + m[key.String()] = jsonValue{value, j.visited} + expectedKey++ key, value = converted.Next(key) } - data, err = json.Marshal(arr) + if isSparseArray { + data, err = json.Marshal(m) + } else { + data, err = json.Marshal(arr) + } case lua.LTString: obj := make(map[string]jsonValue) for key != lua.LNil { diff --git a/json_test.go b/json_test.go index 093810a..147d0f8 100644 --- a/json_test.go +++ b/json_test.go @@ -111,13 +111,23 @@ func TestEncode_SparseArray(t *testing.T) { table string expected string }{ + { + table: `{ + 1, + 2, + 3, + 4, + 5 + }`, + expected: `[1,2,3,4,5]`, + }, { table: `{ 1, 2, [10] = 3 }`, - expected: `[1,2,"[10] = 3"]`, + expected: `{"1":1,"10":3,"2":2}`, }, { table: `{ @@ -125,7 +135,7 @@ func TestEncode_SparseArray(t *testing.T) { [37] = "index 37" } }`, - expected: `{"nested":["[37] = index 37"]}`, + expected: `{"nested":{"37":"index 37"}}`, }, { table: `{ @@ -134,24 +144,35 @@ func TestEncode_SparseArray(t *testing.T) { [37] = "index 37" } }`, - expected: `{"nested":["index 1","[37] = index 37"]}`, + expected: `{"nested":{"1":"index 1","37":"index 37"}}`, }, { table: `{ nested = { - "index 1", - [37] = "index 37" + [37] = "index 37", + "index 1" + } + }`, + expected: `{"nested":{"1":"index 1","37":"index 37"}}`, + }, + { + table: `{ + nested = { + 1, + 2, + 3, + [2] = 4, + [5] = "index 5" } }`, - expected: `{"nested":["index 1","[37] = index 37"]}`, + expected: `{"nested":{"1":1,"2":2,"3":3,"5":"index 5"}}`, // TODO: find out if this is to be expected or `{"nested":{"1":1,"2":4,"3":3,"4":"index 4"}}` }, } + s := lua.NewState() + defer s.Close() + Preload(s) for _, test := range tests { - s := lua.NewState() - defer s.Close() - Preload(s) - luaScript := fmt.Sprintf(` local json = require("json") local t = %s From 7d7969f5c8b43f20a6ca92c315093ab990e23820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20L=C3=BCer?= Date: Mon, 25 Sep 2023 10:41:04 +0200 Subject: [PATCH 7/8] docs: add example --- README.md | 3 ++- doc.go | 51 +++++++++++++++++++++++++++++++-------------------- json_test.go | 9 ++++++++- 3 files changed, 41 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 84fe1bd..401fc0e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ -# gopher-json [![GoDoc](https://godoc.org/layeh.com/gopher-json?status.svg)](https://godoc.org/layeh.com/gopher-json) +# gopher-json [![GoDoc](https://godoc.org/github.com/HannesLueer/gopher-json?status.svg)](https://pkg.go.dev/github.com/HannesLueer/gopher-json) Package json is a simple JSON encoder/decoder for [gopher-lua](https://github.com/yuin/gopher-lua). +It is based on [gopher-json from layeh](https://github.com/layeh/gopher-json) and adds the functionality to encode sparse arrays. ## License diff --git a/doc.go b/doc.go index ecc915d..0f306a6 100644 --- a/doc.go +++ b/doc.go @@ -1,33 +1,44 @@ // Package json is a simple JSON encoder/decoder for gopher-lua. // -// Documentation +// # Documentation // // The following functions are exposed by the library: -// decode(string): Decodes a JSON string. Returns nil and an error string if -// the string could not be decoded. -// encode(value): Encodes a value into a JSON string. Returns nil and an error -// string if the value could not be encoded. +// +// decode(string): Decodes a JSON string. Returns nil and an error string if +// the string could not be decoded. +// encode(value): Encodes a value into a JSON string. Returns nil and an error +// string if the value could not be encoded. // // The following types are supported: // -// Lua | JSON -// ---------+----- -// nil | null -// number | number -// string | string -// table | object: when table is non-empty and has only string keys -// | array: when table is empty, or has only sequential numeric keys -// | starting from 1 +// Lua | JSON +// ---------+----- +// nil | null +// number | number +// string | string +// table | object: when table is non-empty and has only string keys or is a sparse array +// | array: when table is empty, or has only sequential numeric keys +// | starting from 1 // // Attempting to encode any other Lua type will result in an error. // -// Example +// # Example // // Below is an example usage of the library: -// import ( -// luajson "layeh.com/gopher-json" -// ) // -// L := lua.NewState() -// luajson.Preload(L) -package json // import "layeh.com/gopher-json" +// import ( +// "fmt" +// luajson "github.com/HannesLueer/gopher-json" +// lua "github.com/yuin/gopher-lua" +// ) +// +// func main() { +// L := lua.NewState() +// luaScript := "t = {1, 2, [10] = 3}" +// L.DoString(luaScript) +// luaValue := L.GetGlobal("t") +// t, _ := luajson.Encode(luaValue) +// fmt.Printf("t as json: %s", t) +// } + +package json // import "github.com/HannesLueer/gopher-json" diff --git a/json_test.go b/json_test.go index 147d0f8..77f133a 100644 --- a/json_test.go +++ b/json_test.go @@ -165,7 +165,14 @@ func TestEncode_SparseArray(t *testing.T) { [5] = "index 5" } }`, - expected: `{"nested":{"1":1,"2":2,"3":3,"5":"index 5"}}`, // TODO: find out if this is to be expected or `{"nested":{"1":1,"2":4,"3":3,"4":"index 4"}}` + expected: `{"nested":{"1":1,"2":2,"3":3,"5":"index 5"}}`, + }, + { + table: `{ + [65] = 123, + [67] = 456 + }`, + expected: `{"65":123,"67":456}`, }, } From b44b66df6449bb755dad6a19a49a0c606d316c4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20L=C3=BCer?= Date: Mon, 25 Sep 2023 11:12:53 +0200 Subject: [PATCH 8/8] docs: fix indent and module name --- .gitignore | 1 - README.md | 3 +-- doc.go | 54 +++++++++++++++++++++++++++--------------------------- go.mod | 2 +- 4 files changed, 29 insertions(+), 31 deletions(-) delete mode 100644 .gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 485dee6..0000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.idea diff --git a/README.md b/README.md index 401fc0e..84fe1bd 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ -# gopher-json [![GoDoc](https://godoc.org/github.com/HannesLueer/gopher-json?status.svg)](https://pkg.go.dev/github.com/HannesLueer/gopher-json) +# gopher-json [![GoDoc](https://godoc.org/layeh.com/gopher-json?status.svg)](https://godoc.org/layeh.com/gopher-json) Package json is a simple JSON encoder/decoder for [gopher-lua](https://github.com/yuin/gopher-lua). -It is based on [gopher-json from layeh](https://github.com/layeh/gopher-json) and adds the functionality to encode sparse arrays. ## License diff --git a/doc.go b/doc.go index 0f306a6..8f27121 100644 --- a/doc.go +++ b/doc.go @@ -4,21 +4,21 @@ // // The following functions are exposed by the library: // -// decode(string): Decodes a JSON string. Returns nil and an error string if -// the string could not be decoded. -// encode(value): Encodes a value into a JSON string. Returns nil and an error -// string if the value could not be encoded. +// decode(string): Decodes a JSON string. Returns nil and an error string if +// the string could not be decoded. +// encode(value): Encodes a value into a JSON string. Returns nil and an error +// string if the value could not be encoded. // // The following types are supported: // -// Lua | JSON -// ---------+----- -// nil | null -// number | number -// string | string -// table | object: when table is non-empty and has only string keys or is a sparse array -// | array: when table is empty, or has only sequential numeric keys -// | starting from 1 +// Lua | JSON +// ---------+----- +// nil | null +// number | number +// string | string +// table | object: when table is non-empty and has only string keys or is a sparse array +// | array: when table is empty, or has only sequential numeric keys +// | starting from 1 // // Attempting to encode any other Lua type will result in an error. // @@ -26,19 +26,19 @@ // // Below is an example usage of the library: // -// import ( -// "fmt" -// luajson "github.com/HannesLueer/gopher-json" -// lua "github.com/yuin/gopher-lua" -// ) -// -// func main() { -// L := lua.NewState() -// luaScript := "t = {1, 2, [10] = 3}" -// L.DoString(luaScript) -// luaValue := L.GetGlobal("t") -// t, _ := luajson.Encode(luaValue) -// fmt.Printf("t as json: %s", t) -// } +// import ( +// "fmt" +// luajson "github.com/HannesLueer/gopher-json" +// lua "github.com/yuin/gopher-lua" +// ) +// +// func main() { +// L := lua.NewState() +// luaScript := "t = {1, 2, [10] = 3}" +// L.DoString(luaScript) +// luaValue := L.GetGlobal("t") +// t, _ := luajson.Encode(luaValue) +// fmt.Printf("t as json: %s", t) +// } -package json // import "github.com/HannesLueer/gopher-json" +package json // import "layeh.com/gopher-json" diff --git a/go.mod b/go.mod index fa60e0a..a970dc3 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/HannesLueer/gopher-json +module layeh.com/gopher-json go 1.21