LuaJson: Report serialization errors instead of crashing.
This commit is contained in:
parent
471ae7e47b
commit
9eff6f390d
@ -9476,7 +9476,7 @@ end
|
||||
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 =
|
||||
|
@ -2043,8 +2043,13 @@ function HandleConsoleTestJson(a_Split, a_EntireCmd)
|
||||
LOG("Json parsing test succeeded")
|
||||
|
||||
LOG("Testing Json serializing...")
|
||||
local s1 = cJson:Serialize({a = 1, b = "2", c = {3, "4", 5}, d = true}, {indentation = " "})
|
||||
LOG("Serialization result: " .. (s1 or "<nil>"))
|
||||
local s1, msg1 = cJson:Serialize({a = 1, b = "2", c = {3, "4", 5}, d = true}, {indentation = " "})
|
||||
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")
|
||||
|
||||
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.
|
||||
Assumes that a_Value is an array. */
|
||||
@ -136,15 +183,37 @@ static Json::Value JsonSerializeTable(cLuaState & a_LuaState)
|
||||
{
|
||||
int idx;
|
||||
a_LuaState.GetStackValue(-2, idx);
|
||||
try
|
||||
{
|
||||
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
|
||||
{
|
||||
AString name;
|
||||
if (a_LuaState.GetStackValue(-2, name))
|
||||
{
|
||||
try
|
||||
{
|
||||
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);
|
||||
}
|
||||
@ -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:
|
||||
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);
|
||||
|
||||
// Create the writer, with all properties (optional param 3) applied to it:
|
||||
|
Loading…
Reference in New Issue
Block a user