lolly/gjson/gjson.go
xfy 376b9fd33c feat(gjson): add Lua JSON encoding/decoding library
Add gjson package providing high-performance JSON encoding/decoding
for gopher-lua with lua-cjson API compatibility. Uses goccy/go-json
as the underlying JSON engine.

Features:
- Full lua-cjson API compatibility for OpenResty migration
- Sparse array detection and handling
- Maximum nesting depth control for encode/decode
- Number precision control
- Independent configuration instances via gjson.new()
- gjson.null sentinel for JSON null values

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 15:44:31 +08:00

104 lines
3.4 KiB
Go

// Package gjson provides a high-performance JSON encoding/decoding library for gopher-lua.
//
// This package is API-compatible with lua-cjson, allowing easy migration from OpenResty.
// It uses goccy/go-json as the underlying JSON engine for maximum performance.
//
// Basic usage:
//
// L := glua.NewState()
// defer L.Close()
// gjson.Preload(L)
//
// err := L.DoString(`
// local gjson = require("gjson")
// local data = {name = "Alice", age = 30}
// local json_str = gjson.encode(data)
// local decoded = gjson.decode(json_str)
// `)
//
// The package supports:
// - Full lua-cjson API compatibility
// - Sparse array detection and handling
// - Maximum nesting depth control
// - Number precision control
// - Independent configuration instances via gjson.new()
//
// Author: xfy
package gjson
import (
glua "github.com/yuin/gopher-lua"
)
const (
// ModuleName is the Lua module name for require()
ModuleName = "gjson"
// Version is the module version
Version = "1.0.0"
)
// Preload registers the gjson module as a preload in the given LState.
// This allows Lua scripts to use `local gjson = require("gjson")`.
func Preload(L *glua.LState) {
L.PreloadModule(ModuleName, Loader)
}
// Loader is the module loader function called by require("gjson").
func Loader(L *glua.LState) int {
// Create the gjson module table
mod := L.NewTable()
// Create default instance
instance := &GJSON{
config: defaultConfig(),
null: createNull(L),
}
// Register module functions (bound to default instance)
L.SetField(mod, "encode", L.NewFunction(instance.encode))
L.SetField(mod, "decode", L.NewFunction(instance.decode))
L.SetField(mod, "encode_sparse_array", L.NewFunction(instance.cfgEncodeSparseArray))
L.SetField(mod, "encode_max_depth", L.NewFunction(instance.cfgEncodeMaxDepth))
L.SetField(mod, "decode_max_depth", L.NewFunction(instance.cfgDecodeMaxDepth))
L.SetField(mod, "encode_number_precision", L.NewFunction(instance.cfgEncodeNumberPrecision))
L.SetField(mod, "encode_keep_buffer", L.NewFunction(instance.cfgEncodeKeepBuffer))
L.SetField(mod, "new", L.NewFunction(gjsonNew))
// Set gjson.null (lightuserdata representing JSON null)
L.SetField(mod, "null", instance.null)
// Set module metadata
L.SetField(mod, "_NAME", glua.LString(ModuleName))
L.SetField(mod, "_VERSION", glua.LString(Version))
// Push the module table
L.Push(mod)
return 1
}
// RegisterGlobal registers the gjson module as a global variable.
// This allows Lua scripts to use gjson directly without require().
func RegisterGlobal(L *glua.LState) {
mod := L.NewTable()
instance := &GJSON{
config: defaultConfig(),
null: createNull(L),
}
L.SetField(mod, "encode", L.NewFunction(instance.encode))
L.SetField(mod, "decode", L.NewFunction(instance.decode))
L.SetField(mod, "encode_sparse_array", L.NewFunction(instance.cfgEncodeSparseArray))
L.SetField(mod, "encode_max_depth", L.NewFunction(instance.cfgEncodeMaxDepth))
L.SetField(mod, "decode_max_depth", L.NewFunction(instance.cfgDecodeMaxDepth))
L.SetField(mod, "encode_number_precision", L.NewFunction(instance.cfgEncodeNumberPrecision))
L.SetField(mod, "encode_keep_buffer", L.NewFunction(instance.cfgEncodeKeepBuffer))
L.SetField(mod, "new", L.NewFunction(gjsonNew))
L.SetField(mod, "null", instance.null)
L.SetField(mod, "_NAME", glua.LString(ModuleName))
L.SetField(mod, "_VERSION", glua.LString(Version))
L.SetGlobal(ModuleName, mod)
}