Serialization of EO user objects.
This commit is contained in:
parent
cd94be6957
commit
0bd33fe692
16 changed files with 919 additions and 0 deletions
|
|
@ -43,6 +43,7 @@ ADD_SUBDIRECTORY(ga)
|
||||||
ADD_SUBDIRECTORY(gp)
|
ADD_SUBDIRECTORY(gp)
|
||||||
ADD_SUBDIRECTORY(other)
|
ADD_SUBDIRECTORY(other)
|
||||||
ADD_SUBDIRECTORY(utils)
|
ADD_SUBDIRECTORY(utils)
|
||||||
|
ADD_SUBDIRECTORY(serial)
|
||||||
|
|
||||||
IF(ENABLE_PYEO)
|
IF(ENABLE_PYEO)
|
||||||
ADD_SUBDIRECTORY(pyeo)
|
ADD_SUBDIRECTORY(pyeo)
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,9 @@
|
||||||
#include <utils/eoRealVectorBounds.h> // includes eoRealBounds.h
|
#include <utils/eoRealVectorBounds.h> // includes eoRealBounds.h
|
||||||
#include <utils/eoIntBounds.h> // no eoIntVectorBounds
|
#include <utils/eoIntBounds.h> // no eoIntVectorBounds
|
||||||
|
|
||||||
|
// Serialization stuff
|
||||||
|
#include <serial/eoSerial.h>
|
||||||
|
|
||||||
// aliens
|
// aliens
|
||||||
#include <other/external_eo>
|
#include <other/external_eo>
|
||||||
#include <eoCounter.h>
|
#include <eoCounter.h>
|
||||||
|
|
|
||||||
38
eo/src/serial/Array.cpp
Normal file
38
eo/src/serial/Array.cpp
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
# include "Array.h"
|
||||||
|
|
||||||
|
namespace eoserial
|
||||||
|
{
|
||||||
|
|
||||||
|
std::ostream& Array::print( std::ostream& out ) const
|
||||||
|
{
|
||||||
|
out << "[";
|
||||||
|
bool first = true;
|
||||||
|
for (ArrayChildren::const_iterator it = begin(),
|
||||||
|
end = this->end();
|
||||||
|
it != end;
|
||||||
|
++it)
|
||||||
|
{
|
||||||
|
if ( first )
|
||||||
|
{
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
out << ", ";
|
||||||
|
}
|
||||||
|
(*it)->print( out );
|
||||||
|
}
|
||||||
|
out << "]\n";
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array::~Array()
|
||||||
|
{
|
||||||
|
for (ArrayChildren::iterator it = begin(),
|
||||||
|
end = this->end();
|
||||||
|
it != end;
|
||||||
|
++it)
|
||||||
|
{
|
||||||
|
delete *it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace eoserial
|
||||||
149
eo/src/serial/Array.h
Normal file
149
eo/src/serial/Array.h
Normal file
|
|
@ -0,0 +1,149 @@
|
||||||
|
# ifndef __EOSERIAL_ARRAY_H__
|
||||||
|
# define __EOSERIAL_ARRAY_H__
|
||||||
|
|
||||||
|
# include <vector>
|
||||||
|
# include <iostream>
|
||||||
|
|
||||||
|
# include "Entity.h"
|
||||||
|
# include "Serializable.h"
|
||||||
|
|
||||||
|
# include "Object.h"
|
||||||
|
# include "String.h"
|
||||||
|
|
||||||
|
namespace eoserial
|
||||||
|
{
|
||||||
|
|
||||||
|
// Forward declaration for below declarations.
|
||||||
|
class Array;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Declarations of functions present in Utils.h
|
||||||
|
* These are put here to avoid instead of including the file Utils.h, which would
|
||||||
|
* cause a circular inclusion.
|
||||||
|
*/
|
||||||
|
template< class T >
|
||||||
|
void unpack( const Array & array, unsigned int index, T & value );
|
||||||
|
|
||||||
|
void unpackObject( const Array & array, unsigned int index, Persistent & value );
|
||||||
|
|
||||||
|
template< class Container, template<class> class UnpackAlgorithm >
|
||||||
|
void unpackArray( const Array & array, unsigned int index, Container & container );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Represents a JSON array.
|
||||||
|
*
|
||||||
|
* Wrapper for an array, so as to be used as a JSON object.
|
||||||
|
*/
|
||||||
|
class Array : public eoserial::Entity, public std::vector< eoserial::Entity* >
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
typedef std::vector< eoserial::Entity* > ArrayChildren;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Adds the serializable object as a JSON object.
|
||||||
|
* @param obj Object which implemnets JsonSerializable.
|
||||||
|
*/
|
||||||
|
void push_back( const eoserial::Printable* obj )
|
||||||
|
{
|
||||||
|
ArrayChildren::push_back( obj->pack() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Proxy for vector::push_back.
|
||||||
|
*/
|
||||||
|
void push_back( eoserial::Entity* json )
|
||||||
|
{
|
||||||
|
ArrayChildren::push_back( json );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prints the JSON array into the given stream.
|
||||||
|
* @param out The stream
|
||||||
|
*/
|
||||||
|
virtual std::ostream& print( std::ostream& out ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Dtor
|
||||||
|
*/
|
||||||
|
~Array();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following parts allows the user to automatically deserialize an eoserial::Array into a
|
||||||
|
* standard container, by giving the algorithm which will be used to deserialize contained entities.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Functor which determines how to retrieve the real value contained in a eoserial::Entity at
|
||||||
|
* a given place.
|
||||||
|
*
|
||||||
|
* It will be applied for each contained variable in the array.
|
||||||
|
*/
|
||||||
|
template<class Container>
|
||||||
|
struct BaseAlgorithm
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief Main operator.
|
||||||
|
*
|
||||||
|
* @param array The eoserial::Array from which we're reading.
|
||||||
|
* @param i The index of the contained value.
|
||||||
|
* @param container The standard (STL) container in which we'll push back the read value.
|
||||||
|
*/
|
||||||
|
virtual void operator()( const eoserial::Array& array, unsigned int i, Container & container ) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief BaseAlgorithm for retrieving primitive variables.
|
||||||
|
*
|
||||||
|
* This one should be used to retrieve primitive (and types which implement operator>>) variables, for instance
|
||||||
|
* int, double, std::string, etc...
|
||||||
|
*/
|
||||||
|
template<typename C>
|
||||||
|
struct UnpackAlgorithm : public BaseAlgorithm<C>
|
||||||
|
{
|
||||||
|
void operator()( const eoserial::Array& array, unsigned int i, C & container ) const
|
||||||
|
{
|
||||||
|
typename C::value_type t;
|
||||||
|
unpack( array, i, t );
|
||||||
|
container.push_back( t );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief BaseAlgorithm for retrieving eoserial::Persistent objects.
|
||||||
|
*
|
||||||
|
* This one should be used to retrieve objects which implement eoserial::Persistent.
|
||||||
|
*/
|
||||||
|
template<typename C>
|
||||||
|
struct UnpackObjectAlgorithm : public BaseAlgorithm<C>
|
||||||
|
{
|
||||||
|
void operator()( const eoserial::Array& array, unsigned int i, C & container ) const
|
||||||
|
{
|
||||||
|
typename C::value_type t;
|
||||||
|
unpackObject( array, i, t );
|
||||||
|
container.push_back( t );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief General algorithm for array deserialization.
|
||||||
|
*
|
||||||
|
* Applies the BaseAlgorithm to each contained variable in the eoserial::Array.
|
||||||
|
*/
|
||||||
|
template<class Container, template<class T> class UnpackAlgorithm>
|
||||||
|
inline void deserialize( Container & array )
|
||||||
|
{
|
||||||
|
UnpackAlgorithm< Container > algo;
|
||||||
|
for( unsigned int i = 0, size = this->size();
|
||||||
|
i < size;
|
||||||
|
++i)
|
||||||
|
{
|
||||||
|
algo( *this, i, array );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace eoserial
|
||||||
|
|
||||||
|
# endif // __EOSERIAL_ARRAY_H__
|
||||||
|
|
||||||
35
eo/src/serial/CMakeLists.txt
Normal file
35
eo/src/serial/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
######################################################################################
|
||||||
|
### 1) Include the sources
|
||||||
|
######################################################################################
|
||||||
|
|
||||||
|
INCLUDE_DIRECTORIES(${EO_SOURCE_DIR}/src)
|
||||||
|
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
######################################################################################
|
||||||
|
### 2) Define the eoserial target
|
||||||
|
######################################################################################
|
||||||
|
|
||||||
|
SET(EOSERIAL_LIB_OUTPUT_PATH ${EO_BINARY_DIR}/lib)
|
||||||
|
SET(LIBRARY_OUTPUT_PATH ${EOSERIAL_LIB_OUTPUT_PATH})
|
||||||
|
|
||||||
|
SET(EOSERIAL_SOURCES
|
||||||
|
Array.cpp
|
||||||
|
Object.cpp
|
||||||
|
Parser.cpp
|
||||||
|
String.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
ADD_LIBRARY(eoserial STATIC ${EOSERIAL_SOURCES})
|
||||||
|
INSTALL(TARGETS eoserial ARCHIVE DESTINATION lib COMPONENT libraries)
|
||||||
|
|
||||||
|
FILE(GLOB HDRS *.h)
|
||||||
|
INSTALL(FILES ${HDRS} DESTINATION include/eo/serial COMPONENT headers)
|
||||||
|
|
||||||
|
######################################################################################
|
||||||
|
### 3) Optionnal
|
||||||
|
######################################################################################
|
||||||
|
|
||||||
|
SET(EOSERIAL_VERSION ${GLOBAL_VERSION})
|
||||||
|
SET_TARGET_PROPERTIES(eoserial PROPERTIES VERSION "${EOSERIAL_VERSION}")
|
||||||
|
|
||||||
|
######################################################################################
|
||||||
34
eo/src/serial/Entity.h
Normal file
34
eo/src/serial/Entity.h
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
# ifndef __EOSERIAL_ENTITY_H__
|
||||||
|
# define __EOSERIAL_ENTITY_H__
|
||||||
|
|
||||||
|
# include <iostream>
|
||||||
|
# include <sstream>
|
||||||
|
|
||||||
|
namespace eoserial
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief JSON entity
|
||||||
|
*
|
||||||
|
* This class represents a JSON entity, which can be JSON objects,
|
||||||
|
* strings or arrays. It is the base class for the JSON hierarchy.
|
||||||
|
*/
|
||||||
|
class Entity
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual dtor (base class).
|
||||||
|
*/
|
||||||
|
virtual ~Entity() { /* empty */ }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prints the content of a JSON object into a stream.
|
||||||
|
* @param out The stream in which we're printing.
|
||||||
|
*/
|
||||||
|
virtual std::ostream& print( std::ostream& out ) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace eoserial
|
||||||
|
|
||||||
|
# endif // __ENTITY_H__
|
||||||
40
eo/src/serial/Object.cpp
Normal file
40
eo/src/serial/Object.cpp
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
# include "Object.h"
|
||||||
|
|
||||||
|
using namespace eoserial;
|
||||||
|
|
||||||
|
namespace eoserial
|
||||||
|
{
|
||||||
|
|
||||||
|
std::ostream& Object::print( std::ostream& out ) const
|
||||||
|
{
|
||||||
|
out << '{';
|
||||||
|
bool first = true;
|
||||||
|
for(JsonValues::const_iterator it = begin(), end = this->end();
|
||||||
|
it != end;
|
||||||
|
++it)
|
||||||
|
{
|
||||||
|
if ( first )
|
||||||
|
{
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
out << ", ";
|
||||||
|
}
|
||||||
|
|
||||||
|
out << '"' << it->first << "\":"; // key
|
||||||
|
it->second->print( out ); // value
|
||||||
|
}
|
||||||
|
out << "}\n";
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object::~Object()
|
||||||
|
{
|
||||||
|
for(JsonValues::iterator it = begin(), end = this->end();
|
||||||
|
it != end;
|
||||||
|
++it)
|
||||||
|
{
|
||||||
|
delete it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace eoserial
|
||||||
67
eo/src/serial/Object.h
Normal file
67
eo/src/serial/Object.h
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
# ifndef __EOSERIAL_OBJECT_H__
|
||||||
|
# define __EOSERIAL_OBJECT_H__
|
||||||
|
|
||||||
|
# include <map>
|
||||||
|
# include <string>
|
||||||
|
# include <sstream>
|
||||||
|
|
||||||
|
# include "Entity.h"
|
||||||
|
# include "Serializable.h"
|
||||||
|
|
||||||
|
namespace eoserial
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief JSON Object
|
||||||
|
*
|
||||||
|
* This class represents a JSON object, which is basically a dictionnary
|
||||||
|
* of keys (strings) and values (JSON entities).
|
||||||
|
*/
|
||||||
|
class Object : public eoserial::Entity, public std::map< std::string, eoserial::Entity* >
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef std::map<std::string, eoserial::Entity*> JsonValues;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a pair into the JSON object.
|
||||||
|
* @param key The key associated with the eoserial object
|
||||||
|
* @param eoserial The JSON object as created with framework.
|
||||||
|
*/
|
||||||
|
void add( const std::string& key, eoserial::Entity* json )
|
||||||
|
{
|
||||||
|
(*this)[ key ] = json;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a pair into the JSON object.
|
||||||
|
* @param key The key associated with the eoserial object
|
||||||
|
* @param obj A JSON-serializable object
|
||||||
|
*/
|
||||||
|
void add( const std::string& key, const eoserial::Printable* obj )
|
||||||
|
{
|
||||||
|
(*this)[ key ] = obj->pack();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Deserializes a Serializable class instance from this JSON object.
|
||||||
|
* @param obj The object we want to rebuild.
|
||||||
|
*/
|
||||||
|
void deserialize( eoserial::Persistent & obj )
|
||||||
|
{
|
||||||
|
obj.unpack( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Dtor
|
||||||
|
*/
|
||||||
|
~Object();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prints the content of a JSON object into a stream.
|
||||||
|
*/
|
||||||
|
virtual std::ostream& print( std::ostream& out ) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace eoserial
|
||||||
|
# endif // __EOSERIAL_OBJECT_H__
|
||||||
|
|
||||||
153
eo/src/serial/Parser.cpp
Normal file
153
eo/src/serial/Parser.cpp
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
# include <map>
|
||||||
|
# include <string>
|
||||||
|
# include <sstream>
|
||||||
|
# include <vector>
|
||||||
|
|
||||||
|
# include "Parser.h"
|
||||||
|
|
||||||
|
# include "Array.h"
|
||||||
|
# include "Object.h"
|
||||||
|
# include "String.h"
|
||||||
|
|
||||||
|
// in debug mode only
|
||||||
|
// # define DEBUG(x) std::cout << x << std::endl;
|
||||||
|
# define DEBUG(x)
|
||||||
|
|
||||||
|
using namespace eoserial;
|
||||||
|
|
||||||
|
namespace eoserial
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parses a string contained between double quotes.
|
||||||
|
*
|
||||||
|
* Strings can contain escaped double quotes.
|
||||||
|
* @param str The string we're parsing.
|
||||||
|
* @param pos The index of current position in parsed string.
|
||||||
|
* This index will be updated so as to allow the parser to
|
||||||
|
* continue.
|
||||||
|
*/
|
||||||
|
static std::string parseString(const std::string& str, size_t & pos)
|
||||||
|
{
|
||||||
|
// example : "hello"
|
||||||
|
// example 2 : "\"world\""
|
||||||
|
// for hello:
|
||||||
|
// firstQuote == 0, secondQuote == 6
|
||||||
|
// sub string should be from firstQuote+1 to secondQuote-1
|
||||||
|
// so its size should be (secondQuote-1 -(firstQuote+1) + 1)
|
||||||
|
std::string value;
|
||||||
|
size_t firstQuote = str.find( '"', pos );
|
||||||
|
size_t secondQuote;
|
||||||
|
|
||||||
|
/* instead of just seeking the second quote, we need to ensure
|
||||||
|
// that there is no escaped quote before this one.
|
||||||
|
// actually this is harder than that. Using backslashes
|
||||||
|
// to escape double quotes mean that backslashes have to be
|
||||||
|
// escaped to.
|
||||||
|
// example : "text\\" to symbolize : text\
|
||||||
|
// example : "text\\\" to symbolize : text\"
|
||||||
|
// In fact, we should find if number of backslashes is odd; in this case,
|
||||||
|
// the double quotes are escaped and we should find the next one.
|
||||||
|
*/
|
||||||
|
int backslashesCount;
|
||||||
|
do {
|
||||||
|
++pos;
|
||||||
|
secondQuote = str.find( '"', pos );
|
||||||
|
size_t i = secondQuote - 1;
|
||||||
|
|
||||||
|
// Find the backslashes
|
||||||
|
backslashesCount = 0;
|
||||||
|
while ( str[ i ] == '\\' )
|
||||||
|
{
|
||||||
|
--i;
|
||||||
|
++backslashesCount;
|
||||||
|
}
|
||||||
|
pos = secondQuote;
|
||||||
|
} while( backslashesCount % 2 == 1 );
|
||||||
|
|
||||||
|
value = str.substr( firstQuote+1, secondQuote-firstQuote-1 );
|
||||||
|
pos = secondQuote + 1;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Moves the given index pos to the next character which is
|
||||||
|
* neither a coma, a space nor a new line.
|
||||||
|
*
|
||||||
|
* @param str The string in which we want to ignores those characters.
|
||||||
|
* @param pos The index of current position in parsed string.
|
||||||
|
*/
|
||||||
|
static void ignoreChars(const std::string& str, size_t & pos)
|
||||||
|
{
|
||||||
|
// ignore white spaces and comas
|
||||||
|
for (char current = str[ pos ];
|
||||||
|
current == ',' || current == ' ' || current == '\n';
|
||||||
|
current = str[ ++pos ]);
|
||||||
|
}
|
||||||
|
|
||||||
|
String* Parser::parseJsonString(const std::string & str, size_t & pos)
|
||||||
|
{
|
||||||
|
return new String( parseString( str, pos ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
Object* Parser::parse(const std::string & str)
|
||||||
|
{
|
||||||
|
size_t initial(0); // we begin at position 0
|
||||||
|
return static_cast<Object*>( parseRight(str, initial) );
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity* Parser::parseRight(const std::string & str, size_t & pos)
|
||||||
|
{
|
||||||
|
Entity* value = 0;
|
||||||
|
|
||||||
|
if ( str[ pos ] == '{' )
|
||||||
|
{
|
||||||
|
// next one is an object
|
||||||
|
DEBUG("We read an object.")
|
||||||
|
Object* obj = new Object;
|
||||||
|
pos += 1;
|
||||||
|
while( pos < str.size() && str[ pos ] != '}' )
|
||||||
|
{
|
||||||
|
parseLeft( str, pos, obj );
|
||||||
|
ignoreChars( str, pos );
|
||||||
|
}
|
||||||
|
DEBUG("We just finished to read an object ! ")
|
||||||
|
pos += 1; // we're on the }, go to the next char
|
||||||
|
value = obj;
|
||||||
|
}
|
||||||
|
else if ( str[ pos ] == '"' )
|
||||||
|
{
|
||||||
|
// next one is a string
|
||||||
|
DEBUG("We read a string")
|
||||||
|
value = parseJsonString( str, pos );
|
||||||
|
}
|
||||||
|
else if ( str[ pos ] == '[' )
|
||||||
|
{
|
||||||
|
// next one is an array
|
||||||
|
DEBUG("We read an array")
|
||||||
|
Array* array = new Array;
|
||||||
|
pos += 1;
|
||||||
|
while( pos < str.size() && str[ pos ] != ']' )
|
||||||
|
{
|
||||||
|
Entity* child = parseRight( str, pos );
|
||||||
|
if ( child )
|
||||||
|
array->push_back( child );
|
||||||
|
}
|
||||||
|
DEBUG("We've finished to read our array.")
|
||||||
|
pos += 1; // we're on the ], go to the next char
|
||||||
|
value = array;
|
||||||
|
}
|
||||||
|
ignoreChars( str, pos );
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Parser::parseLeft(const std::string & str, size_t & pos, Object* eoserial)
|
||||||
|
{
|
||||||
|
std::string key = parseString(str, pos);
|
||||||
|
++pos; // the colon
|
||||||
|
DEBUG("We've read the key ")
|
||||||
|
(*eoserial)[ key ] = parseRight( str, pos );
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace eoserial
|
||||||
|
|
||||||
78
eo/src/serial/Parser.h
Normal file
78
eo/src/serial/Parser.h
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
# ifndef __EOSERIAL_PARSER_H__
|
||||||
|
# define __EOSERIAL_PARSER_H__
|
||||||
|
|
||||||
|
# include "Entity.h"
|
||||||
|
# include "String.h"
|
||||||
|
# include "Object.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file contains a tiny JSON parser used in DAE. This parser just handles
|
||||||
|
* a subset of JSON grammar, with the following restrictions :
|
||||||
|
* - all strings must be surrounded by double quotes.
|
||||||
|
* - everything which is not an object or an array is considered to be a string
|
||||||
|
* (even integers, booleans,...).
|
||||||
|
* - no syntax check is done. We trust the programmer and he has to ensure that
|
||||||
|
* every JSON string he produces is valid.
|
||||||
|
*
|
||||||
|
* @author Benjamin BOUVIER
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace eoserial
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parser from a JSON source.
|
||||||
|
*
|
||||||
|
* This parser does just retrieve values and does NOT check the structure of
|
||||||
|
* the input. This implies that if the input is not correct, the result is undefined
|
||||||
|
* and can result to a failure on execution.
|
||||||
|
*/
|
||||||
|
class Parser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parses the given string and returns the JSON object read.
|
||||||
|
*/
|
||||||
|
static eoserial::Object* parse(const std::string & str);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parses the right part of a JSON object as a string.
|
||||||
|
*
|
||||||
|
* The right part of an object can be a string (for instance :
|
||||||
|
* "key":"value"), a JSON array (for instance: "key":["1"]) or
|
||||||
|
* another JSON object (for instance: "key":{"another_key":"value"}).
|
||||||
|
*
|
||||||
|
* The right parts are found after keys (which are parsed by parseLeft)
|
||||||
|
* and in arrays.
|
||||||
|
*
|
||||||
|
* @param str The string we're parsing.
|
||||||
|
* @param pos The index of the current position in the string.
|
||||||
|
* @return The JSON object matching the right part.
|
||||||
|
*/
|
||||||
|
static eoserial::Entity* parseRight(const std::string & str, size_t & pos);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parses the left value of a key-value pair, which is the key.
|
||||||
|
*
|
||||||
|
* @param str The string we're parsing.
|
||||||
|
* @param pos The index of the current position in the string.
|
||||||
|
* @param eoserial The current JSON object for which we're adding a key-value pair.
|
||||||
|
*/
|
||||||
|
static void parseLeft(const std::string & str, size_t & pos, eoserial::Object* json);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retrieves a string in a JSON content.
|
||||||
|
*
|
||||||
|
* @param str The string we're parsing.
|
||||||
|
* @param pos The index of the current position of parsing,
|
||||||
|
* which will be updated.
|
||||||
|
*/
|
||||||
|
static eoserial::String* parseJsonString(const std::string & str, size_t & pos);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace eoserial
|
||||||
|
|
||||||
|
# endif // __EOSERIAL_PARSER_H__
|
||||||
44
eo/src/serial/Serializable.h
Normal file
44
eo/src/serial/Serializable.h
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
# ifndef __EOSERIAL_SERIALIZABLE_H__
|
||||||
|
# define __EOSERIAL_SERIALIZABLE_H__
|
||||||
|
|
||||||
|
# include <string>
|
||||||
|
|
||||||
|
namespace eoserial
|
||||||
|
{
|
||||||
|
|
||||||
|
class Object; // to avoid recursive inclusion with JsonObject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Interface showing that object can be written to a eoserial type
|
||||||
|
* (currently JSON).
|
||||||
|
*/
|
||||||
|
class Printable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Serializes the object to JSON format.
|
||||||
|
* @return A JSON object created with new.
|
||||||
|
*/
|
||||||
|
virtual eoserial::Object* pack() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Interface showing that object can be eoserialized (written and read
|
||||||
|
* from an input).
|
||||||
|
*
|
||||||
|
* Note : Persistent objects should have a default non-arguments constructor.
|
||||||
|
*/
|
||||||
|
class Persistent : public Printable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Loads class fields from a JSON object.
|
||||||
|
* @param json A JSON object. Programmer doesn't have to delete it, it
|
||||||
|
* is automatically done.
|
||||||
|
*/
|
||||||
|
virtual void unpack(const eoserial::Object* json) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace eoserial
|
||||||
|
|
||||||
|
# endif // __EOSERIAL_SERIALIZABLE_H__
|
||||||
11
eo/src/serial/String.cpp
Normal file
11
eo/src/serial/String.cpp
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# include "String.h"
|
||||||
|
|
||||||
|
namespace eoserial
|
||||||
|
{
|
||||||
|
std::ostream& String::print( std::ostream& out ) const
|
||||||
|
{
|
||||||
|
out << '"' << *this << '"';
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
} // namespace eoserial
|
||||||
|
|
||||||
79
eo/src/serial/String.h
Normal file
79
eo/src/serial/String.h
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
# ifndef __EOSERIAL_STRING_H__
|
||||||
|
# define __EOSERIAL_STRING_H__
|
||||||
|
|
||||||
|
# include <string>
|
||||||
|
# include <sstream>
|
||||||
|
|
||||||
|
# include "Entity.h"
|
||||||
|
|
||||||
|
namespace eoserial
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief JSON String
|
||||||
|
*
|
||||||
|
* Wrapper for string, so as to be used as a JSON object.
|
||||||
|
*/
|
||||||
|
class String : public eoserial::Entity, public std::string
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Default ctor.
|
||||||
|
* @param str The string we want to wrap.
|
||||||
|
*/
|
||||||
|
String( const std::string& str ) : std::string( str ) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Ctor used only on parsing.
|
||||||
|
*/
|
||||||
|
String( ) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prints out the string.
|
||||||
|
*/
|
||||||
|
virtual std::ostream& print( std::ostream& out ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Deserializes the current String into a given primitive type value.
|
||||||
|
* @param value The value in which we're writing.
|
||||||
|
*/
|
||||||
|
template<class T>
|
||||||
|
inline void deserialize( T & value );
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Copy and reaffectation are forbidden
|
||||||
|
explicit String( const String& _ );
|
||||||
|
String& operator=( const String& _ );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Casts a eoserial::String into a primitive value, or in a type which at
|
||||||
|
* least overload operator>>.
|
||||||
|
*
|
||||||
|
* @param value A reference to the variable we're writing into.
|
||||||
|
*
|
||||||
|
* It's not necessary to specify the variable type, which can be infered by compiler when
|
||||||
|
* invoking.
|
||||||
|
*/
|
||||||
|
template<class T>
|
||||||
|
inline void String::deserialize( T & value )
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << *this;
|
||||||
|
ss >> value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Specialization for strings, which don't need to be converted through
|
||||||
|
* a stringstream.
|
||||||
|
*/
|
||||||
|
template<>
|
||||||
|
inline void String::deserialize( std::string & value )
|
||||||
|
{
|
||||||
|
value = *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace eoserial
|
||||||
|
|
||||||
|
# endif // __EOSERIAL_STRING_H__
|
||||||
167
eo/src/serial/Utils.h
Normal file
167
eo/src/serial/Utils.h
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
# ifndef __EOSERIAL_UTILS_H__
|
||||||
|
# define __EOSERIAL_UTILS_H__
|
||||||
|
|
||||||
|
# include "Array.h"
|
||||||
|
# include "Object.h"
|
||||||
|
# include "String.h"
|
||||||
|
|
||||||
|
namespace eoserial
|
||||||
|
{
|
||||||
|
/*****************************
|
||||||
|
* DESERIALIZATION FUNCTIONS *
|
||||||
|
*****************************
|
||||||
|
These functions are useful for casting eoserial::objects into simple, primitive
|
||||||
|
variables or into class instance which implement eoserial::Persistent.
|
||||||
|
|
||||||
|
The model is always quite the same :
|
||||||
|
- the first argument is the containing object (which is a eoserial::Entity,
|
||||||
|
an object or an array)
|
||||||
|
- the second argument is the key or index,
|
||||||
|
- the last argument is the value in which we're writing.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template< class T >
|
||||||
|
inline void unpack( const Object & obj, const std::string & key, T & value )
|
||||||
|
{
|
||||||
|
static_cast<String*>( obj.find( key )->second )->deserialize( value );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void unpackObject( const Object & obj, const std::string & key, Persistent & value )
|
||||||
|
{
|
||||||
|
static_cast<Object*>( obj.find( key )->second )->deserialize( value );
|
||||||
|
}
|
||||||
|
|
||||||
|
template< class Container, template<class> class UnpackAlgorithm >
|
||||||
|
inline void unpackArray( const Object & obj, const std::string & key, Container & array )
|
||||||
|
{
|
||||||
|
static_cast<Array*>( obj.find( key )->second )->deserialize< Container, UnpackAlgorithm >( array );
|
||||||
|
}
|
||||||
|
|
||||||
|
template< class T >
|
||||||
|
inline void unpack( const Array & array, unsigned int index, T & value )
|
||||||
|
{
|
||||||
|
static_cast<String*>( array[ index ] )->deserialize( value );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void unpackObject( const Array & array, unsigned int index, Persistent & value )
|
||||||
|
{
|
||||||
|
static_cast<Object*>( array[ index ] )->deserialize( value );
|
||||||
|
}
|
||||||
|
|
||||||
|
template< class Container, template<class> class UnpackAlgorithm >
|
||||||
|
inline void unpackArray( const Array & array, unsigned int index, Container & container )
|
||||||
|
{
|
||||||
|
static_cast<Array*>( array[ index ] )->deserialize< Container, UnpackAlgorithm >( container );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************
|
||||||
|
*** SERIALIZATION FUNCTIONS ***
|
||||||
|
*******************************
|
||||||
|
These functions are useful for casting classic objects and
|
||||||
|
eoserial::Persistent objects into eoserial entities which
|
||||||
|
can be manipulated by the framework.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Casts a value of a stream-serializable type (i.e, which implements
|
||||||
|
* operator <<) into a JsonString.
|
||||||
|
*
|
||||||
|
* This is used when serializing the objects : all primitives types should be
|
||||||
|
* converted into strings to get more easily manipulated.
|
||||||
|
*
|
||||||
|
* @param value The value we're converting.
|
||||||
|
* @return JsonString wrapper for the value.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
String* make( const T & value )
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << value;
|
||||||
|
return new String( ss.str() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Specialization for strings : no need to convert as they're still
|
||||||
|
* usable as strings.
|
||||||
|
*/
|
||||||
|
template<>
|
||||||
|
inline String* make( const std::string & value )
|
||||||
|
{
|
||||||
|
return new String( value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These functions are useful for automatically serializing STL containers into
|
||||||
|
* eoserial arrays which could be used by the framework.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Functor which explains how to push the value into the eoserial::Array.
|
||||||
|
*/
|
||||||
|
template< class T >
|
||||||
|
struct PushAlgorithm
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief Main operator.
|
||||||
|
*
|
||||||
|
* @param array The eoserial::array in which we're writing.
|
||||||
|
* @param value The variable we are writing.
|
||||||
|
*/
|
||||||
|
virtual void operator()( Array & array, const T & value ) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Push algorithm for primitive variables.
|
||||||
|
*
|
||||||
|
* This one should be used when inserting primitive (and types which implement
|
||||||
|
* operator<<) variables.
|
||||||
|
*/
|
||||||
|
template< class T >
|
||||||
|
struct MakeAlgorithm : public PushAlgorithm<T>
|
||||||
|
{
|
||||||
|
void operator()( Array & array, const T & value )
|
||||||
|
{
|
||||||
|
array.push_back( make( value ) );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Push algorithm for eoserial::Persistent variables.
|
||||||
|
*/
|
||||||
|
template< class T >
|
||||||
|
struct SerializablePushAlgorithm : public PushAlgorithm<T>
|
||||||
|
{
|
||||||
|
void operator()( Array & array, const T & obj )
|
||||||
|
{
|
||||||
|
// obj address is not saved into array.push_back.
|
||||||
|
array.push_back( &obj );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Casts a STL container (vector<int> or list<std::string>, for instance)
|
||||||
|
* into a eoserial::Array.
|
||||||
|
*
|
||||||
|
* @þaram PushAlgorithm The algorithm used for inserting new element in the eoserial::Array.
|
||||||
|
* This algorithm is directly called, so it is its own charge to invoke push_back on the
|
||||||
|
* eoserial::Array.
|
||||||
|
*/
|
||||||
|
template< class Container, template<class> class PushAlgorithm >
|
||||||
|
Array* makeArray( const Container & array )
|
||||||
|
{
|
||||||
|
Array* returned_array = new Array;
|
||||||
|
typedef typename Container::const_iterator iterator;
|
||||||
|
typedef typename Container::value_type Type;
|
||||||
|
PushAlgorithm< Type > algo;
|
||||||
|
for (
|
||||||
|
iterator it = array.begin(), end = array.end();
|
||||||
|
it != end;
|
||||||
|
++it)
|
||||||
|
{
|
||||||
|
algo( *returned_array, *it );
|
||||||
|
}
|
||||||
|
return returned_array;
|
||||||
|
}
|
||||||
|
} // namespace eoserial
|
||||||
|
|
||||||
|
# endif //__EOSERIAL_UTILS_H__
|
||||||
12
eo/src/serial/eoSerial.h
Normal file
12
eo/src/serial/eoSerial.h
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
# ifndef __EOSERIAL_HEADERS__
|
||||||
|
# define __EOSERIAL_HEADERS__
|
||||||
|
|
||||||
|
# include "Object.h"
|
||||||
|
# include "Serializable.h"
|
||||||
|
# include "Array.h"
|
||||||
|
# include "Object.h"
|
||||||
|
# include "String.h"
|
||||||
|
# include "Parser.h"
|
||||||
|
# include "Utils.h"
|
||||||
|
|
||||||
|
# endif // __EOSERIAL_HEADERS__
|
||||||
8
eo/src/serial/json_example
Normal file
8
eo/src/serial/json_example
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{"a":"b",
|
||||||
|
"obj":
|
||||||
|
{"obj_a":"obj_}b","subobj_a":
|
||||||
|
{"subk":"subv"}
|
||||||
|
},
|
||||||
|
"c":"d",
|
||||||
|
"array":["1","2",{"\"array\"_obj\"":"array_ov]"}, ["3"], "4"]
|
||||||
|
}
|
||||||
Reference in a new issue