[c++] Fix SimpleJSON map deserializtion

Correctly handle issues when deserializing invalid map data in
SimpleJSON protocol, specifically:

* When keys aren't of primitive type
* When there isn't a matching value for evey key

Also:

* Change bond::basic_string_stream operator << to support various forms
  of std::basic_string<char>
* Fix ElementNotFoundException to work with wstrings
This commit is contained in:
Chad Walters 2017-07-11 15:31:18 -07:00 коммит произвёл Christopher Warrington
Родитель 3d67501110
Коммит d80983ac3f
4 изменённых файлов: 78 добавлений и 21 удалений

Просмотреть файл

@ -23,6 +23,10 @@ different versioning scheme, following the Haskell community's
* When Unicode conversion fails during JSON deserialization to wstring, a
bond::CoreException is now thrown instead of a Boost exception.
* When SimpleJSON deserializes a map key with no matching value, a
bond::CoreException is now thrown.
* When SimpleJSON deserializes a map key of non-primitive type, a
bond::CoreException is now thrown.
### C# ###

Просмотреть файл

@ -41,7 +41,8 @@ public:
return *this;
}
basic_string_stream& operator<<(const std::string& str)
template<typename T, typename A>
basic_string_stream& operator<<(const std::basic_string<char, T, A>& str)
{
write(str.begin(), str.end());
return *this;

Просмотреть файл

@ -5,6 +5,8 @@
#include "detail/string_stream.h"
#include <bond/core/bond_types.h>
#include <boost/locale/encoding_utf.hpp>
#include <boost/utility/enable_if.hpp>
#define BOND_THROW(x, y) throw x((::bond::detail::basic_string_stream<1024>() << y).content());
@ -55,26 +57,65 @@ BOND_NORETURN inline void MergerContainerException(uint32_t payload, uint32_t ob
}
BOND_NORETURN inline void InvalidKeyTypeException()
{
BOND_THROW(CoreException,
"Map key type not valid");
}
namespace detail
{
template <typename Key>
BOND_NORETURN inline void ElementNotFoundExceptionHelper(
const Key& key,
typename boost::enable_if<is_wstring<Key>>::type* = nullptr)
{
try
{
BOND_THROW(CoreException,
"Map element not found: key: " <<
boost::locale::conv::utf_to_utf<char>(
string_data(key),
string_data(key) + string_length(key),
boost::locale::conv::stop));
}
catch (boost::locale::conv::conversion_error &)
{
BOND_THROW(CoreException, "Map element not found: key: <bad wstring>");
}
}
template <typename Key>
BOND_NORETURN inline void ElementNotFoundExceptionHelper(
const Key& key,
typename boost::disable_if<is_wstring<Key>>::type* = nullptr)
{
BOND_THROW(CoreException,
"Map element not found: key: " << key);
}
}
template <typename Key>
BOND_NORETURN inline void ElementNotFoundException(const Key& key)
{
BOND_THROW(CoreException,
"Map element not found: key: " << key);
detail::ElementNotFoundExceptionHelper(key);
}
BOND_NORETURN inline void UnknownProtocolException()
{
BOND_THROW(CoreException,
"Unmarshaling failed: unsupported protocol");
"Unmarshaling failed: unsupported protocol");
}
BOND_NORETURN inline void UnknownProtocolException(uint16_t magic)
{
BOND_THROW(CoreException,
"Unsupported protocol: "
<< (char)(magic & 0xFF) << (char)(magic >> 8));
"Unsupported protocol: "
<< (char)(magic & 0xFF) << (char)(magic >> 8));
}
@ -87,14 +128,14 @@ BOND_NORETURN inline void NothingException()
BOND_NORETURN inline void InvalidEnumValueException(const char* value, const char* enum_)
{
BOND_THROW(bond::CoreException,
BOND_THROW(bond::CoreException,
"Unexpected value " << value << " for enum " << enum_);
}
BOND_NORETURN inline void InvalidEnumValueException(int32_t value, const char* enum_)
{
BOND_THROW(bond::CoreException,
BOND_THROW(bond::CoreException,
"Unexpected value " << value << " for enum " << enum_);
}
@ -106,7 +147,7 @@ BOND_NORETURN inline void RapidJsonException(const char* error, size_t offset)
}
BOND_NORETURN inline void UnicodeConversionException(void)
BOND_NORETURN inline void UnicodeConversionException()
{
BOND_THROW(CoreException,
"Unicode conversion exception");
@ -135,8 +176,8 @@ BOND_NORETURN
inline void StructBaseDifferentException(const StructDef& src,
const StructDef& dst)
{
BOND_THROW(SchemaValidateException,
"Schemas are incompatible; struct base different: "
BOND_THROW(SchemaValidateException,
"Schemas are incompatible; struct base different: "
<< src.metadata.name << ", " << dst.metadata.name);
}
@ -145,8 +186,8 @@ BOND_NORETURN
inline void RequiredFieldMissingException(const StructDef& s_dst,
const FieldDef& f_dst)
{
BOND_THROW(SchemaValidateException,
"Schemas are incompatible; required field missing: "
BOND_THROW(SchemaValidateException,
"Schemas are incompatible; required field missing: "
<< s_dst.metadata.name << "::" << f_dst.metadata.name);
}
@ -157,9 +198,9 @@ inline void OptionalToRequiredException(const StructDef& s_src,
const FieldDef& f_src,
const FieldDef& f_dst)
{
BOND_THROW(SchemaValidateException,
"Schemas are incompatible; required modifier removed: "
<< s_src.metadata.name << "::" << f_src.metadata.name << ", "
BOND_THROW(SchemaValidateException,
"Schemas are incompatible; required modifier removed: "
<< s_src.metadata.name << "::" << f_src.metadata.name << ", "
<< s_dst.metadata.name << "::" << f_dst.metadata.name);
}
@ -170,16 +211,16 @@ inline void FieldTypeIncompatibleException(const StructDef& s_src,
const FieldDef& f_src,
const FieldDef& f_dst)
{
BOND_THROW(SchemaValidateException,
"Schemas are incompatible; field types incompatible: "
<< s_src.metadata.name << "::" << f_src.metadata.name << ", "
BOND_THROW(SchemaValidateException,
"Schemas are incompatible; field types incompatible: "
<< s_src.metadata.name << "::" << f_src.metadata.name << ", "
<< s_dst.metadata.name << "::" << f_dst.metadata.name);
}
BOND_NORETURN inline void UnknownSchemaDefException(uint16_t id)
{
BOND_THROW(SchemaValidateException,
BOND_THROW(SchemaValidateException,
"Failed to validate schema compatibility; "
"SchemaDef contains unknown field: " << id);
}

Просмотреть файл

@ -150,8 +150,19 @@ DeserializeMap(X& var, BondDataType keyType, const T& element, SimpleJsonReader<
{
detail::Read(*it, key);
}
else
{
bond::InvalidKeyTypeException();
}
SimpleJsonReader<Buffer> input(reader, *++it);
++it;
if (it == end)
{
bond::ElementNotFoundException(key);
}
SimpleJsonReader<Buffer> input(reader, *it);
if (value_type.ComplexTypeMatch(*it))
detail::MakeValue(input, element).template Deserialize<Protocols>(mapped_at(var, key));