316 lines
6.3 KiB
C++
316 lines
6.3 KiB
C++
|
|
||
|
// LuaJson.cpp
|
||
|
|
||
|
// Implements the Json exposure bindings to Lua
|
||
|
|
||
|
#include "Globals.h"
|
||
|
#include <sstream>
|
||
|
#include "LuaJson.h"
|
||
|
#include "LuaState.h"
|
||
|
#include "tolua++/include/tolua++.h"
|
||
|
#include "json/json.h"
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// fwd:
|
||
|
|
||
|
static void PushJsonValue(const Json::Value & a_Value, cLuaState & a_LuaState);
|
||
|
static Json::Value JsonSerializeValue(cLuaState & a_LuaState);
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/** Pushes the specified Json array as a table on top of the specified Lua state.
|
||
|
Assumes that a_Value is an array. */
|
||
|
static void PushJsonArray(const Json::Value & a_Value, cLuaState & a_LuaState)
|
||
|
{
|
||
|
// Create the appropriately-sized Lua table:
|
||
|
lua_createtable(a_LuaState, static_cast<int>(a_Value.size()), 0);
|
||
|
|
||
|
// Insert each value to the appropriate index (1-based):
|
||
|
int idx = 1;
|
||
|
for (const auto & v: a_Value)
|
||
|
{
|
||
|
// Include Json null values in the array - it will have holes, but indices will stay the same
|
||
|
PushJsonValue(v, a_LuaState);
|
||
|
lua_rawseti(a_LuaState, -2, idx);
|
||
|
idx += 1;
|
||
|
} // for v: a_Value[]
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/** Pushes the specified Json object as a table on top of the specified Lua state.
|
||
|
Assumes that a_Value is an object. */
|
||
|
static void PushJsonObject(const Json::Value & a_Value, cLuaState & a_LuaState)
|
||
|
{
|
||
|
// Create the appropriately-sized Lua table:
|
||
|
lua_createtable(a_LuaState, 0, static_cast<int>(a_Value.size()));
|
||
|
|
||
|
// Json::Value has no means of iterating over children with their names included.
|
||
|
// We need to iterate over names and "find" them in the object again:
|
||
|
auto names = a_Value.getMemberNames();
|
||
|
for (const auto & n: names)
|
||
|
{
|
||
|
auto v = a_Value[n];
|
||
|
if (v.isNull())
|
||
|
{
|
||
|
// Skip null values
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Set the value in Lua's table:
|
||
|
a_LuaState.Push(n);
|
||
|
PushJsonValue(v, a_LuaState);
|
||
|
lua_rawset(a_LuaState, -3);
|
||
|
} // for itr - a_Value[]
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/** Pushes the specified Json value as an appropriate type on top of the specified Lua state. */
|
||
|
void PushJsonValue(const Json::Value & a_Value, cLuaState & a_LuaState)
|
||
|
{
|
||
|
switch (a_Value.type())
|
||
|
{
|
||
|
case Json::nullValue:
|
||
|
{
|
||
|
a_LuaState.PushNil();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case Json::intValue:
|
||
|
case Json::uintValue:
|
||
|
case Json::realValue:
|
||
|
{
|
||
|
a_LuaState.Push(static_cast<lua_Number>(a_Value.asDouble()));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case Json::booleanValue:
|
||
|
{
|
||
|
a_LuaState.Push(a_Value.asBool());
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case Json::stringValue:
|
||
|
{
|
||
|
a_LuaState.Push(a_Value.asString());
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case Json::arrayValue:
|
||
|
{
|
||
|
PushJsonArray(a_Value, a_LuaState);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case Json::objectValue:
|
||
|
{
|
||
|
PushJsonObject(a_Value, a_LuaState);
|
||
|
break;
|
||
|
}
|
||
|
} // switch (v.type())
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/** Serializes the Lua table at the top of the specified Lua state's stack into a Json value.
|
||
|
Lets jsoncpp decides whether to serialize into an object or an array. */
|
||
|
static Json::Value JsonSerializeTable(cLuaState & a_LuaState)
|
||
|
{
|
||
|
Json::Value res;
|
||
|
lua_pushnil(a_LuaState);
|
||
|
while (lua_next(a_LuaState, -2) != 0)
|
||
|
{
|
||
|
if (lua_type(a_LuaState, -2) == LUA_TNUMBER)
|
||
|
{
|
||
|
int idx;
|
||
|
a_LuaState.GetStackValue(-2, idx);
|
||
|
res[idx - 1] = JsonSerializeValue(a_LuaState);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
AString name;
|
||
|
if (a_LuaState.GetStackValue(-2, name))
|
||
|
{
|
||
|
res[name] = JsonSerializeValue(a_LuaState);
|
||
|
}
|
||
|
}
|
||
|
lua_pop(a_LuaState, 1);
|
||
|
}
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/** Serializes the Lua value at the top of the specified Lua state into a Json value. */
|
||
|
static Json::Value JsonSerializeValue(cLuaState & a_LuaState)
|
||
|
{
|
||
|
switch (lua_type(a_LuaState, -1))
|
||
|
{
|
||
|
case LUA_TNUMBER:
|
||
|
{
|
||
|
lua_Number v;
|
||
|
a_LuaState.GetStackValue(-1, v);
|
||
|
return Json::Value(v);
|
||
|
}
|
||
|
case LUA_TSTRING:
|
||
|
{
|
||
|
AString v;
|
||
|
a_LuaState.GetStackValue(-1, v);
|
||
|
return Json::Value(v);
|
||
|
}
|
||
|
case LUA_TTABLE:
|
||
|
{
|
||
|
return JsonSerializeTable(a_LuaState);
|
||
|
}
|
||
|
default:
|
||
|
{
|
||
|
LOGD("Attempting to serialize an unhandled Lua value type: %d", lua_type(a_LuaState, -1));
|
||
|
return Json::Value(Json::nullValue);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
static int tolua_cJson_Parse(lua_State * a_LuaState)
|
||
|
{
|
||
|
// Function signature:
|
||
|
// cJson:Parse("string") -> table
|
||
|
|
||
|
// Check the param types:
|
||
|
cLuaState L(a_LuaState);
|
||
|
if (
|
||
|
!L.CheckParamUserTable(1, "cJson") ||
|
||
|
!L.CheckParamString(2) ||
|
||
|
!L.CheckParamEnd(3)
|
||
|
)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Get the input string:
|
||
|
AString input;
|
||
|
if (!L.GetStackValue(2, input))
|
||
|
{
|
||
|
LOGWARNING("cJson:Parse(): Cannot read input string");
|
||
|
L.LogStackTrace();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Parse the string:
|
||
|
Json::Value root;
|
||
|
Json::Reader reader;
|
||
|
if (!reader.parse(input, root, false))
|
||
|
{
|
||
|
L.PushNil();
|
||
|
L.Push(Printf("Parsing Json failed: %s", reader.getFormattedErrorMessages().c_str()));
|
||
|
return 2;
|
||
|
}
|
||
|
|
||
|
// Push the Json value onto Lua stack:
|
||
|
PushJsonValue(root, L);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
static int tolua_cJson_Serialize(lua_State * a_LuaState)
|
||
|
{
|
||
|
// Function signature:
|
||
|
// cJson:Serialize({table}, [{option1 = value1, option2 = value2}]) -> string
|
||
|
|
||
|
// Check the param types:
|
||
|
cLuaState L(a_LuaState);
|
||
|
if (
|
||
|
!L.CheckParamUserTable(1, "cJson") ||
|
||
|
!L.CheckParamTable(2) ||
|
||
|
!L.CheckParamEnd(4)
|
||
|
)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Push the table to the top of the Lua stack, and call the serializing function:
|
||
|
lua_pushvalue(L, 2);
|
||
|
Json::Value root = JsonSerializeValue(L);
|
||
|
lua_pop(L, 1);
|
||
|
|
||
|
// Create the writer, with all properties (optional param 3) applied to it:
|
||
|
Json::StreamWriterBuilder builder;
|
||
|
if (lua_istable(L, 3))
|
||
|
{
|
||
|
lua_pushnil(L);
|
||
|
while (lua_next(L, -2) != 0)
|
||
|
{
|
||
|
if (lua_type(L, -2) == LUA_TSTRING)
|
||
|
{
|
||
|
AString propName, propValue;
|
||
|
if (L.GetStackValues(-2, propName, propValue))
|
||
|
{
|
||
|
builder[propName] = propValue;
|
||
|
}
|
||
|
}
|
||
|
lua_pop(L, 1);
|
||
|
}
|
||
|
// Check for invalid settings:
|
||
|
Json::Value invalid;
|
||
|
if (!builder.validate(&invalid))
|
||
|
{
|
||
|
LOGINFO("cJson:Serialize(): detected invalid settings:");
|
||
|
for (const auto & n: invalid.getMemberNames())
|
||
|
{
|
||
|
LOGINFO(" \"%s\" (\"%s\")", n.c_str(), invalid[n].asCString());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
auto writer(builder.newStreamWriter());
|
||
|
|
||
|
// Serialize the string and push it as the return value:
|
||
|
std::stringstream ss;
|
||
|
writer->write(root, &ss);
|
||
|
L.Push(ss.str());
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
void cLuaJson::Bind(cLuaState & a_LuaState)
|
||
|
{
|
||
|
tolua_beginmodule(a_LuaState, nullptr);
|
||
|
|
||
|
// Create the cJson API class:
|
||
|
tolua_usertype(a_LuaState, "cJson");
|
||
|
tolua_cclass(a_LuaState, "cJson", "cJson", "", nullptr);
|
||
|
|
||
|
// Fill in the functions (alpha-sorted):
|
||
|
tolua_beginmodule(a_LuaState, "cJson");
|
||
|
tolua_function(a_LuaState, "Parse", tolua_cJson_Parse);
|
||
|
tolua_function(a_LuaState, "Serialize", tolua_cJson_Serialize);
|
||
|
tolua_endmodule(a_LuaState);
|
||
|
tolua_endmodule(a_LuaState);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|