2017-10-28 16:23:39 +02:00
# ifndef REFLECTIVE_RAPIDJSON_JSON_ERROR_HANDLING_H
# define REFLECTIVE_RAPIDJSON_JSON_ERROR_HANDLING_H
/*!
* \ file errorhandling . h
* \ brief Contains helper for error handling when deserializing JSON files .
*/
# include <c++utilities/conversion/types.h>
# include <c++utilities/misc/traits.h>
# include <rapidjson/rapidjson.h>
2017-10-28 18:24:12 +02:00
# include <limits>
2017-10-28 16:23:39 +02:00
# include <list>
# include <string>
# include <vector>
namespace ReflectiveRapidJSON {
/*!
* \ brief The JsonDeserializationErrorKind enum specifies which kind of error happend when populating variables from parsing results .
*/
enum class JsonDeserializationErrorKind : byte {
2017-11-03 17:45:16 +01:00
TypeMismatch , /**< The expected type does not match the type actually present in the JSON document. */
ArraySizeMismatch , /**< The expected array size does not match the actual size of the JSON array. A fixed size is expected when deserializing an std::tuple. */
ConversionError , /**< The expected type matches the type present in the JSON document, but further conversion of the value failed. */
2017-10-28 16:23:39 +02:00
} ;
/*!
* \ brief The JsonType enum specifies the JSON data type .
* \ remarks This is currently only used for error handling to propagate expected and actual types in case of a mismatch .
*/
enum class JsonType : byte {
Null ,
Number ,
Bool ,
String ,
Array ,
Object ,
} ;
2017-11-03 17:45:16 +01:00
// define helper functions which return the JsonType for the C++ type specified as template parameter
2017-10-28 16:23:39 +02:00
template < typename Type ,
Traits : : EnableIf < Traits : : Not < std : : is_same < Type , bool > > , Traits : : Any < std : : is_integral < Type > , std : : is_floating_point < Type > > > . . . >
constexpr JsonType jsonType ( )
{
return JsonType : : Number ;
}
template < typename Type , Traits : : EnableIfAny < std : : is_same < Type , bool > > . . . > constexpr JsonType jsonType ( )
{
return JsonType : : Bool ;
}
template < typename Type , Traits : : EnableIfAny < Traits : : IsString < Type > , Traits : : IsCString < Type > > . . . > constexpr JsonType jsonType ( )
{
return JsonType : : String ;
}
template < typename Type , Traits : : EnableIf < Traits : : IsIteratable < Type > , Traits : : Not < Traits : : IsString < Type > > > . . . > constexpr JsonType jsonType ( )
{
return JsonType : : Array ;
}
template < typename Type ,
Traits : : DisableIfAny < std : : is_integral < Type > , std : : is_floating_point < Type > , Traits : : IsString < Type > , Traits : : IsCString < Type > ,
Traits : : IsIteratable < Type > > . . . >
constexpr JsonType jsonType ( )
{
return JsonType : : Object ;
}
/*!
* \ brief Maps the type info provided by RapidJSON to JsonType .
*/
constexpr JsonType jsonType ( RAPIDJSON_NAMESPACE : : Type type )
{
switch ( type ) {
case RAPIDJSON_NAMESPACE : : kFalseType :
case RAPIDJSON_NAMESPACE : : kTrueType :
return JsonType : : Bool ;
case RAPIDJSON_NAMESPACE : : kObjectType :
return JsonType : : Object ;
case RAPIDJSON_NAMESPACE : : kArrayType :
return JsonType : : Array ;
case RAPIDJSON_NAMESPACE : : kStringType :
return JsonType : : String ;
case RAPIDJSON_NAMESPACE : : kNumberType :
return JsonType : : Number ;
default :
return JsonType : : Null ;
}
}
/*!
* \ brief The JsonDeserializationError struct describes any errors of fromJson ( ) except such caused by invalid JSON .
*/
struct JsonDeserializationError {
JsonDeserializationError ( JsonDeserializationErrorKind kind , JsonType expectedType , JsonType actualType , const char * record ,
const char * member = nullptr , std : : size_t index = noIndex ) ;
/// \brief Which kind of error occured.
JsonDeserializationErrorKind kind ;
/// \brief The expected type (might not be relevant for all error kinds).
JsonType expectedType ;
/// \brief The actual type (might not be relevant for all error kinds).
JsonType actualType ;
/// \brief The name of the class or struct which was being processed when the error was ascertained.
const char * record ;
/// \brief The name of the member which was being processed when the error was ascertained.
const char * member ;
/// \brief The index in the array which was being processed when the error was ascertained.
std : : size_t index ;
/// \brief Indicates no array was being processed when the error occured.
static constexpr std : : size_t noIndex = std : : numeric_limits < std : : size_t > : : max ( ) ;
} ;
/*!
* \ brief Constructs a new JsonDeserializationError .
* \ remarks Supposed to be called by JsonDeserializationErrors : : reportTypeMismatch ( ) and similar methods of JsonDeserializationErrors .
*/
inline JsonDeserializationError : : JsonDeserializationError (
JsonDeserializationErrorKind kind , JsonType expectedType , JsonType actualType , const char * record , const char * member , std : : size_t index )
: kind ( kind )
, expectedType ( expectedType )
, actualType ( actualType )
, record ( record )
, member ( member )
, index ( index )
{
}
/*!
* \ brief The JsonDeserializationErrors struct can be passed to fromJson ( ) for error handling .
*
* When passed to fromJson ( ) and an error occurs , a JsonDeserializationError is added to this object .
* If throwOn is set , the JsonDeserializationError is additionally thrown making the error fatal .
*
* \ remarks Errors due to invalid JSON always lead to a RAPIDJSON_NAMESPACE : : ParseResult object being thrown . So this
* only concerns errors listed in JsonDeserializationErrorKind .
*/
struct JsonDeserializationErrors : public std : : vector < JsonDeserializationError > {
JsonDeserializationErrors ( ) ;
template < typename ExpectedType > void reportTypeMismatch ( RAPIDJSON_NAMESPACE : : Type presentType ) ;
2017-10-29 22:53:02 +01:00
void reportArraySizeMismatch ( ) ;
2017-11-03 17:45:16 +01:00
void reportConversionError ( JsonType jsonType ) ;
2017-10-28 16:23:39 +02:00
/// \brief The name of the class or struct which is currently being processed.
const char * currentRecord ;
/// \brief The name of the member (in currentRecord) which is currently being processed.
const char * currentMember ;
/// \brief The index in the array which is currently processed.
std : : size_t currentIndex ;
/// \brief The list of fatal error types in form of flags.
2017-11-03 17:45:16 +01:00
enum class ThrowOn : byte { None = 0 , TypeMismatch = 0x1 , ArraySizeMismatch = 0x2 , ConversionError = 0x4 } throwOn ;
private :
void throwMaybe ( ThrowOn on ) const ;
2017-10-28 16:23:39 +02:00
} ;
/*!
* \ brief Creates an empty JsonDeserializationErrors object with default context and no errors considered fatal .
*/
inline JsonDeserializationErrors : : JsonDeserializationErrors ( )
: currentRecord ( " [document] " )
, currentMember ( nullptr )
, currentIndex ( JsonDeserializationError : : noIndex )
, throwOn ( ThrowOn : : None )
{
}
/*!
* \ brief Combines to ThrowOn values .
*/
constexpr JsonDeserializationErrors : : ThrowOn operator | ( JsonDeserializationErrors : : ThrowOn lhs , JsonDeserializationErrors : : ThrowOn rhs )
{
2017-11-03 17:45:16 +01:00
return static_cast < JsonDeserializationErrors : : ThrowOn > ( static_cast < byte > ( lhs ) | static_cast < byte > ( rhs ) ) ;
}
/*!
* \ brief Throws the last error if its type is considered critical .
* \ param on Specifies the type of the last error as ThrowOn mask .
* \ remarks Behaviour is undefined if no error is present .
*/
inline void JsonDeserializationErrors : : throwMaybe ( ThrowOn on ) const
{
if ( static_cast < byte > ( throwOn ) & static_cast < byte > ( on ) ) {
throw back ( ) ;
}
2017-10-28 16:23:39 +02:00
}
/*!
2017-10-29 22:53:02 +01:00
* \ brief Reports a type mismatch between \ tparam ExpectedType and \ a presentType within the current context .
2017-10-28 16:23:39 +02:00
*/
template < typename ExpectedType > inline void JsonDeserializationErrors : : reportTypeMismatch ( RAPIDJSON_NAMESPACE : : Type presentType )
{
emplace_back (
JsonDeserializationErrorKind : : TypeMismatch , jsonType < ExpectedType > ( ) , jsonType ( presentType ) , currentRecord , currentMember , currentIndex ) ;
2017-11-03 17:45:16 +01:00
throwMaybe ( ThrowOn : : TypeMismatch ) ;
2017-10-28 16:23:39 +02:00
}
2017-10-29 22:53:02 +01:00
/*!
* \ brief Reports an array size mismatch .
* \ todo Allow specifying expected and actual size .
* \ remarks This can happen when mapping a JSON array to a C + + tuple .
*/
inline void JsonDeserializationErrors : : reportArraySizeMismatch ( )
{
emplace_back ( JsonDeserializationErrorKind : : ArraySizeMismatch , JsonType : : Array , JsonType : : Array , currentRecord , currentMember , currentIndex ) ;
2017-11-03 17:45:16 +01:00
throwMaybe ( ThrowOn : : ArraySizeMismatch ) ;
}
/*!
* \ brief Reports a conversion error . An error of that kind occurs when the JSON type matched the expected type , but further conversion of the value has failed .
* \ todo Allow specifying the error message .
* \ remarks This can happen when doing custom mapping ( eg . when interpreting a JSON string as time value ) .
*/
inline void JsonDeserializationErrors : : reportConversionError ( JsonType jsonType )
{
emplace_back ( JsonDeserializationErrorKind : : ConversionError , jsonType , jsonType , currentRecord , currentMember , currentIndex ) ;
throwMaybe ( ThrowOn : : ConversionError ) ;
2017-10-29 22:53:02 +01:00
}
2017-10-28 16:23:39 +02:00
} // namespace ReflectiveRapidJSON
# endif // REFLECTIVE_RAPIDJSON_JSON_REFLECTOR_H