LuaJson: Report serialization errors instead of crashing.
This commit is contained in:
parent
471ae7e47b
commit
9eff6f390d
@ -9476,7 +9476,7 @@ end
|
|||||||
Type = "string",
|
Type = "string",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Notes = "Serializes the input table into a Json string. The options table, if present, is used to adjust the formatting of the serialized string, see below for details.",
|
Notes = "Serializes the input table into a Json string. The options table, if present, is used to adjust the formatting of the serialized string, see below for details. <br/>Returns nil and error message if the table cannot be serialized (eg. contains both an array part and a dictionary part).",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
AdditionalInfo =
|
AdditionalInfo =
|
||||||
|
@ -2043,8 +2043,13 @@ function HandleConsoleTestJson(a_Split, a_EntireCmd)
|
|||||||
LOG("Json parsing test succeeded")
|
LOG("Json parsing test succeeded")
|
||||||
|
|
||||||
LOG("Testing Json serializing...")
|
LOG("Testing Json serializing...")
|
||||||
local s1 = cJson:Serialize({a = 1, b = "2", c = {3, "4", 5}, d = true}, {indentation = " "})
|
local s1, msg1 = cJson:Serialize({a = 1, b = "2", c = {3, "4", 5}, d = true}, {indentation = " "})
|
||||||
LOG("Serialization result: " .. (s1 or "<nil>"))
|
LOG("Serialization result: " .. (s1 or ("ERROR: " .. (msg1 or "<no message>"))))
|
||||||
|
|
||||||
|
local s2, msg2 = cJson:Serialize({valueA = 1, valueB = {3, badValue = "4", 5}, d = true}, {indentation = " "})
|
||||||
|
assert(not(s2), "Serialization should have failed")
|
||||||
|
LOG("Serialization correctly failed with message: " .. (msg2 or "<no message>"))
|
||||||
|
|
||||||
LOG("Json serializing test succeeded")
|
LOG("Json serializing test succeeded")
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
@ -22,6 +22,53 @@ static Json::Value JsonSerializeValue(cLuaState & a_LuaState);
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** Exception thrown when the input cannot be serialized.
|
||||||
|
Keeps track of the error message and the problematic value's path in the table.
|
||||||
|
*/
|
||||||
|
class CannotSerializeException:
|
||||||
|
public std::runtime_error
|
||||||
|
{
|
||||||
|
typedef std::runtime_error Super;
|
||||||
|
public:
|
||||||
|
/** Constructs a new instance of the exception based on the provided values directly. */
|
||||||
|
explicit CannotSerializeException(const AString & a_ValueName, const char * a_ErrorMsg):
|
||||||
|
Super(a_ErrorMsg),
|
||||||
|
m_ValueName(a_ValueName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constructs a new instance of the exception based on the provided values directly. */
|
||||||
|
explicit CannotSerializeException(int a_ValueIndex, const char * a_ErrorMsg):
|
||||||
|
Super(a_ErrorMsg),
|
||||||
|
m_ValueName(Printf("%d", a_ValueIndex))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constructs a new instance of the exception that takes the error message and value name from the parent, and prefix the value name with the specified prefix.
|
||||||
|
Used for prefixing the value name's path along the call stack that lead to the main exception. */
|
||||||
|
explicit CannotSerializeException(const CannotSerializeException & a_Parent, const AString & a_ValueNamePrefix):
|
||||||
|
Super(a_Parent.what()),
|
||||||
|
m_ValueName(a_ValueNamePrefix + "." + a_Parent.m_ValueName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constructs a new instance of the exception that takes the error message and value name from the parent, and prefix the value name with the specified prefix.
|
||||||
|
Used for prefixing the value name's path along the call stack that lead to the main exception. */
|
||||||
|
explicit CannotSerializeException(const CannotSerializeException & a_Parent, int a_ValueNamePrefixIndex):
|
||||||
|
Super(a_Parent.what()),
|
||||||
|
m_ValueName(Printf("%d", a_ValueNamePrefixIndex) + "." + a_Parent.m_ValueName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const AString & GetValueName() const { return m_ValueName; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
AString m_ValueName;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** Pushes the specified Json array as a table on top of the specified Lua state.
|
/** Pushes the specified Json array as a table on top of the specified Lua state.
|
||||||
Assumes that a_Value is an array. */
|
Assumes that a_Value is an array. */
|
||||||
@ -136,15 +183,37 @@ static Json::Value JsonSerializeTable(cLuaState & a_LuaState)
|
|||||||
{
|
{
|
||||||
int idx;
|
int idx;
|
||||||
a_LuaState.GetStackValue(-2, idx);
|
a_LuaState.GetStackValue(-2, idx);
|
||||||
|
try
|
||||||
|
{
|
||||||
res[idx - 1] = JsonSerializeValue(a_LuaState);
|
res[idx - 1] = JsonSerializeValue(a_LuaState);
|
||||||
}
|
}
|
||||||
|
catch (const CannotSerializeException & exc)
|
||||||
|
{
|
||||||
|
throw CannotSerializeException(exc, idx);
|
||||||
|
}
|
||||||
|
catch (const std::exception & exc) // Cannot catch Json::Exception, because it's not properly defined
|
||||||
|
{
|
||||||
|
throw CannotSerializeException(idx, exc.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AString name;
|
AString name;
|
||||||
if (a_LuaState.GetStackValue(-2, name))
|
if (a_LuaState.GetStackValue(-2, name))
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
res[name] = JsonSerializeValue(a_LuaState);
|
res[name] = JsonSerializeValue(a_LuaState);
|
||||||
}
|
}
|
||||||
|
catch (const CannotSerializeException & exc)
|
||||||
|
{
|
||||||
|
throw CannotSerializeException(exc, name);
|
||||||
|
}
|
||||||
|
catch (const std::exception & exc) // Cannot catch Json::Exception, because it's not properly defined
|
||||||
|
{
|
||||||
|
throw CannotSerializeException(name, exc.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
lua_pop(a_LuaState, 1);
|
lua_pop(a_LuaState, 1);
|
||||||
}
|
}
|
||||||
@ -259,7 +328,17 @@ static int tolua_cJson_Serialize(lua_State * a_LuaState)
|
|||||||
|
|
||||||
// Push the table to the top of the Lua stack, and call the serializing function:
|
// Push the table to the top of the Lua stack, and call the serializing function:
|
||||||
lua_pushvalue(L, 2);
|
lua_pushvalue(L, 2);
|
||||||
Json::Value root = JsonSerializeValue(L);
|
Json::Value root;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
root = JsonSerializeValue(L);
|
||||||
|
}
|
||||||
|
catch (const CannotSerializeException & exc)
|
||||||
|
{
|
||||||
|
lua_pushnil(L);
|
||||||
|
L.Push(Printf("Cannot serialize into Json, value \"%s\" caused an error \"%s\"", exc.GetValueName().c_str(), exc.what()));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
|
||||||
// Create the writer, with all properties (optional param 3) applied to it:
|
// Create the writer, with all properties (optional param 3) applied to it:
|
||||||
|
Loading…
Reference in New Issue
Block a user