Skip to content
Browse files

Added more comments and README

  • Loading branch information...
1 parent a5bd759 commit 68852d4e6c00dacb3a2d38ea953d9bfabd6ce739 @martin-seomoz martin-seomoz committed Apr 24, 2012
Showing with 217 additions and 43 deletions.
  1. +79 −0 README
  2. +42 −29 Serialize/JsonSerializer.h
  3. +21 −2 Serialize/JsonSerializerContainer.h
  4. +25 −12 Serialize/JsonSerializerMap.h
  5. +50 −0 test.cpp
View
79 README
@@ -0,0 +1,79 @@
+
+Yet another JSON serialization library for C++
+
+Objective:
+
+ The objective is to make serialization/de-serialization of C++ object to/from JSON trivial.
+
+ This means:
+ 1) does not build a JSON object. Reads data directly into C++ object.
+ 2) In normal usage (in the majority of situations) there should be NO need to write any code.
+ 3) User should not need to understand JSON or validate its input.
+ 4) Should work seamlessly with streams.
+ 5) Standard containers should automatically work
+
+ I am not concerned about speed.
+ Though my trivial test work just fine in terms of speed.
+
+ The design was done with the primary goal of communicating with WEB-Servers that speak JSON.
+ The main envisioned usage was for mobile devices were many small JSON objects are transfered in both directions.
+
+Example: (see code in test.cpp for full code)
+
+ /* A class that you want to serialize. */
+ class MyClass
+ {
+ int data1;
+ float data2;
+ std::string data3;
+
+ // This is only required if the members are private.
+ friend struct ThorsAnvil::Serialize::Json::JsonSerializeTraits<MyClass>;
+ };
+
+ /*
+ * Though there is no code involved, you do need to set up
+ * this structure to tell the library what fields need to be serialized.
+ */
+ namespace ThorsAnvil { namespace Serialize { namespace Json {
+ template<>
+ struct JsonSerializeTraits<MyClass>
+ {
+ static JsonSerializeType const type = Map; // This says serialize as a JSON object.
+
+ THORSANVIL_SERIALIZE_JsonAttribute(MyClass, data1);
+ THORSANVIL_SERIALIZE_JsonAttribute(MyClass, data2);
+ THORSANVIL_SERIALIZE_JsonAttribute(MyClass, data3);
+
+ // This type indicates what fields need to be serialize/de-serialzied.
+ typedef boost::mpl::vector<data1, data2, data3> SerializeInfo;
+ };
+ }}}
+
+ # Build
+ > g++ -o test -I build/include3rd/ -I build/include/ test.cpp -L build/lib/ -lserialize -ljson
+
+ The MyClass object is serialize as:
+ {"data1": 56 ,"data2": 23.456 ,"data3": "Hi there" }
+
+
+Build instructions:
+
+ # Set up required 3rd party libraries boost/gtest
+ cd third
+ ./setup
+
+
+ # The following builds the Json parser and installs the
+ # release and debug libraries into build/lib and external headers into build/include
+ cd ../Json
+ make install
+
+ # The following builds the Serialization libs and installs the
+ # release and debug libraries into build/lib and external headers into build/include
+ cd ../Serialize
+ make install
+
+
+
+
View
71 Serialize/JsonSerializer.h
@@ -25,10 +25,9 @@
* class MyClass
* {
* // STUFF
-
+ *
* // If any members are private and need to be serialized then
* // JsonSerializeTraits<MyClass> must be a friend of the class so it can generate the appropriate code
- * // The macro THORSANVIL_SERIALIZE_JsonAttribute will try to access these members (See below)
*
* friend class JsonSerializeTraits<MyClass>;
* };
@@ -37,36 +36,15 @@
* template<>
* class JsonSerializeTraits<MyClass>
* {
+ * static ThorsAnvil::Serialize::Json::JsonSerializeType const type = Map;
+ *
* THORSANVIL_SERIALIZE_JsonAttribute(MyClass, member1);
* THORSANVIL_SERIALIZE_JsonAttribute(MyClass, member2);
* THORSANVIL_SERIALIZE_JsonAttribute(MyClass, member3);
* typedef boost::mps::vector<member1, member2, member3> SerializeInfo;
* };
* }}}
*
- * If you can not access members directly (ie you are trying to write serialization to code that is not yours (std::vector))
- * Then you will need to write your own accessors type to simulate them members you want to serialize:
- *
- * namespace ThorsAnvil { namespace Serialize { namespace Json {
- * template<typename T>
- * class JsonSerializeTraits<std::vector<T> >
- * {
- * MIY TODO Example needs updating.
- * The classes have changed slightly.
- *
- * // Can not access the members of vector directly.
- * // So we serialize it as two elements:
- * // A size property (so we can reserve space (if we see it first)
- * // An elements property. This serialize the elemnts as a Json Array
- * // For each of these we need to write specialized class's to access the public API
- * // See ArraySizeAccessor and ArrayElementAccessor for a detailed example
- * //
- * THORSANVIL_SERIALIZE_JsonAttributeAccess(std::vector<T>, size, ArraySizeAccessor);
- * THORSANVIL_SERIALIZE_JsonAttributeAccess(std::vector<T>, elements, ArrayElementAccessor);
- * typedef boost::mps::vector<size, elements> SerializeInfo;
- * };
- * }}}
- *
* Now we can serialize/deserialize with:
* std::cout << jsonExport(myClassObj) << "\n";
* std::cin >> jsonInport(myClassObj);
@@ -90,6 +68,25 @@
#include <iostream>
+/*
+ * Helper Macros:
+ *
+ * These are macros that will build some boilerplate types needed by the serialization code.
+ *
+ * THORSANVIL_SERIALIZE_JsonAttribute: This is the main macro used.
+ * It identifies a class member that will be serialized
+ *
+ * THORSANVIL_SERIALIZE_JsonAttribute_1: Used internally (should probably not be used by others).
+ * THORSANVIL_SERIALIZE_JsonAttributeAccess: If you want to run some code to as part of the serialization processes
+ * this macro allows you to specify a type that will be used during serialization.
+ * Examples will be provided in the documentaion.
+ *
+ * THORSANVIL_SERIALIZE_JsonGenericMapAttributeAccess: A generic accessor can be used to generate multiple items.
+ * When de-serializing the Json can be applied to multiple elements.
+ * Used manly for container classes like std::map
+ *THORSANVIL_SERIALIZE_JsonGenericArrAttributeAccess: A generic accessor used by for arrays rather than maps (std::vector)
+ * But otherwise identical to THORSANVIL_SERIALIZE_JsonGenericMapAttributeAccess
+ */
#define THORSANVIL_SERIALIZE_JsonAttribute(className, member) \
typedef BOOST_TYPEOF(((className*)01)->member) JsonAttribute ## member ## Type; \
THORSANVIL_SERIALIZE_JsonAttribute_1(className, member, JsonSerializeTraits<JsonAttribute ## member ## Type>)
@@ -131,7 +128,7 @@ namespace ThorsAnvil
{
namespace Serialize
{
-
+ /* External dependencies from the generic Serialization code */
template<typename T, typename Parser>
struct Importer;
@@ -141,7 +138,16 @@ namespace ThorsAnvil
namespace Json
{
+/* Three basic element types: Invalid (this obejct is not a top level JSON object)
+ * Map A JSON object { [<string> : <value> [, <string> : <value>]*]? }
+ * Array A JSON array [ [<value> [, <value>]*]? ]
+ */
enum JsonSerializeType {Invalid, Map, Array};
+
+/*
+ * All objects that want to be serialized by this code must define their own specialization of this class.
+ * The default version will cause compilation errors. Which hopefully will bring the reader here.
+ */
template<typename T>
struct JsonSerializeTraits
{
@@ -219,6 +225,7 @@ struct JsonSerialize;
template<typename T, typename A, typename RegisterKey>
struct JsonSerialize<T, A, RegisterKey, Map>
{
+ // Generic serialization of a JSON object
static void activate(JsonSerializeItem<T, A, RegisterKey> const& item, std::ostream& stream, T const& src)
{
if (!item.first)
@@ -231,6 +238,7 @@ struct JsonSerialize<T, A, RegisterKey, Map>
template<typename C, typename A, typename RegisterKey>
struct JsonSerialize<C, A, RegisterKey, Array>
{
+ // Generic serialization of a JSON array
static void activate(JsonSerializeItem<C, A, RegisterKey> const& item, std::ostream& stream, C const& src)
{
item.accessor.serialize(src, stream);
@@ -246,6 +254,11 @@ struct JsonDeSerialize
parser.registerAction(item.memberName, action);
}
};
+
+/*
+ * A type holder object that picks up the correct versions of JsonSerialize and JsonDeSerialize
+ * Used by MPLForEachActivateItem to get the correct type
+ */
template<typename T, typename A, typename RegisterKey>
struct JsonSerializeItem
{
@@ -283,9 +296,9 @@ class JsonImportAction: public ThorsAnvil::Json::SaxAction
{}
virtual void doPreAction(ThorsAnvil::Json::ScannerSax&, ThorsAnvil::Json::Key const&){}
- // Read fundamental type directly into the member
virtual void doAction(ThorsAnvil::Json::ScannerSax&, ThorsAnvil::Json::Key const&, JsonValue const& value)
{
+ // Read fundamental type directly into the member
memberRef = value.getValue<I>();
}
};
@@ -299,10 +312,10 @@ class JsonImportAction<SerializeInfo, I, false>: public ThorsAnvil::Json::SaxAct
{}
virtual void doAction(ThorsAnvil::Json::ScannerSax&, ThorsAnvil::Json::Key const&, JsonValue const&){}
- // Compound type. Register callback for each member.
- // This is done when the attribute is reached in json not before
virtual void doPreAction(ThorsAnvil::Json::ScannerSax& parser, ThorsAnvil::Json::Key const&)
{
+ // Compound type. Register callback for each member.
+ // This is done when the attribute is reached in json not before
boost::mpl::for_each<SerializeInfo>(MPLForEachActivateItem<I, ThorsAnvil::Json::ScannerSax>(parser, memberRef));
}
};
View
23 Serialize/JsonSerializerContainer.h
@@ -11,16 +11,29 @@ namespace ThorsAnvil
namespace Json
{
-
+/*
+ * To generalize the code below we use the ContainerTraits to get type information about the container.
+ */
template<typename C>
struct ContainerTraits;
+/*
+ * We have two main container types isConstContainer = true/false
+ * isConstContainer(true): This means the member of the container are constant. (std::map<> std::set<>) etc.
+ * Thus we can not create a member in the container then read data directly into this member.
+ * We must create a temporary object read the JSON data into this temporary then insert this into the container.
+ * isConstContainer(false): This means the member of the container are NOT constant (std::vector<>, std::list<>, std::deque<>)
+ * This mean we can insert a default object into the container as soon as possable.
+ * The JSON reading code will then de-serialize directly into the object in the container.
+ */
template<typename SerializeInfo, typename C, bool EnablePod = boost::is_fundamental<typename ContainerTraits<C>::DataType>::value, bool isConstContainer = ContainerTraits<C>::isConstContainer>
class JsonContainerImportAction;
template<typename SerializeInfo, typename C>
class JsonContainerImportAction<SerializeInfo, C, false, true>: public ThorsAnvil::Json::SaxAction
{
+ // Class for reading into a const container.
+ // That contains objects that are not a fundamental JSON type (not int/float/string etc)
typedef C LocalType;
typedef typename ContainerTraits<C>::ValueType ValueType;
LocalType& destination;
@@ -44,6 +57,8 @@ class JsonContainerImportAction<SerializeInfo, C, false, true>: public ThorsAnvi
template<typename SerializeInfo, typename C>
class JsonContainerImportAction<SerializeInfo, C, true, true>: public ThorsAnvil::Json::SaxAction
{
+ // Class for reading into a const container.
+ // That contains objects that are a fundamental JSON type (int/float/string etc)
C& destination;
public:
JsonContainerImportAction(C& dst)
@@ -61,6 +76,8 @@ class JsonContainerImportAction<SerializeInfo, C, true, true>: public ThorsAnvil
template<typename SerializeInfo, typename C>
class JsonContainerImportAction<SerializeInfo, C, false, false>: public ThorsAnvil::Json::SaxAction
{
+ // Class for reading into a mutable container
+ // That contains objects that are a NOT fundamental JSON type (not int/float/string etc)
typedef C LocalType;
typedef typename ContainerTraits<C>::DataType DataType;
LocalType& destination;
@@ -82,6 +99,8 @@ class JsonContainerImportAction<SerializeInfo, C, false, false>: public ThorsAnv
template<typename SerializeInfo, typename C>
class JsonContainerImportAction<SerializeInfo, C, true, false>: public ThorsAnvil::Json::SaxAction
{
+ // Class for reading into a mutable container
+ // That contains objects that are a fundamental JSON type (int/float/string etc)
typedef C LocalType;
typedef typename ContainerTraits<C>::DataType DataType;
LocalType& destination;
@@ -106,7 +125,7 @@ ThorsAnvil::Json::SaxAction* new_JsonImportAction(C& dst)
return new JsonContainerImportAction<SerializeInfo,C>(dst);
}
-
+/* Specialization of JsonContainerAttributeAccessor used for containers. */
template<typename C>
class JsonContainerAttributeAccessor
{
View
37 Serialize/JsonSerializerMap.h
@@ -8,21 +8,46 @@
#include <map>
+/*
+ * There are a couple of specializations for std::map to make serialization to JSON more natural.
+ *
+ * A JSON object uses a string as a key.
+ * This if we have a std::map<> that uses a std::string as a key then it maps directly to a JSON object.
+ *
+ * Any other type of std::map<> will be mapped as JSON array. Where each element in the array is JSON object
+ * that looks like this { "first": <Key>, "second": <value> }
+ */
namespace ThorsAnvil
{
namespace Serialize
{
namespace Json
{
+/*
+ * Default definition for map
+ */
template<typename K, typename V>
struct ContainerTraits<std::map<K,V> >
{
static bool const isConstContainer = true;
typedef V DataType;
typedef std::pair<K,V> ValueType;
};
+template<typename K, typename V>
+struct JsonSerializeTraits<std::map<K, V> >
+{
+ static JsonSerializeType const type = Array;
+ typedef std::map<K, V> LocalType;
+ typedef JsonContainerAttributeAccessor<LocalType> Accessor;
+ THORSANVIL_SERIALIZE_JsonGenericArrAttributeAccess(LocalType, Accessor);
+ typedef boost::mpl::vector<genericAccessor> SerializeInfo;
+};
+
+/*
+ * The following are specialization for maps that use std::string as they key.
+ */
template<typename V, typename A, typename RegisterKey>
struct JsonSerialize<std::map<std::string,V>, A, RegisterKey, Map>
{
@@ -87,21 +112,9 @@ class JsonContainerImportAction<SerializeInfo, std::map<std::string, V>, false,
{
boost::mpl::for_each<SerializeInfo>(MPLForEachActivateItem<V, ThorsAnvil::Json::ScannerSax>(parser, destination[key.mapKey]));
}
- // Read fundamental type directly into the member
virtual void doAction(ThorsAnvil::Json::ScannerSax&, ThorsAnvil::Json::Key const&, JsonValue const&)
{}
};
-template<typename K, typename V>
-struct JsonSerializeTraits<std::map<K, V> >
-{
- static JsonSerializeType const type = Array;
-
- typedef std::map<K, V> LocalType;
- typedef JsonContainerAttributeAccessor<LocalType> Accessor;
- THORSANVIL_SERIALIZE_JsonGenericArrAttributeAccess(LocalType, Accessor);
- typedef boost::mpl::vector<genericAccessor> SerializeInfo;
-};
-
template<typename V>
struct JsonSerializeTraits<std::map<std::string, V> >
{
View
50 test.cpp
@@ -0,0 +1,50 @@
+ #include <iostream>
+ #include <string>
+ #include "serialize/json.h"
+
+ /* A class that you want to serialize. */
+ class MyClass
+ {
+ int data1;
+ float data2;
+ std::string data3;
+
+ // This is only required if the members are private.
+ friend struct ThorsAnvil::Serialize::Json::JsonSerializeTraits<MyClass>;
+ public:
+ MyClass(int a, float b, std::string const& c): data1(a), data2(b), data3(c) {}
+ friend std::ostream& operator<<(std::ostream& stream, MyClass const& value)
+ {
+ // Not need for serialization but we need to print out the results to show it worked.
+ return stream << value.data1 << " : " << value.data2 << " : " << value.data3;
+ }
+ };
+
+ /*
+ * Though there is no code involved, you do need to set up
+ * this structure to tell the library what fields need to be serialized.
+ */
+ namespace ThorsAnvil { namespace Serialize { namespace Json {
+ template<>
+ struct JsonSerializeTraits<MyClass>
+ {
+ static JsonSerializeType const type = Array;
+
+ THORSANVIL_SERIALIZE_JsonAttribute(MyClass, data1);
+ THORSANVIL_SERIALIZE_JsonAttribute(MyClass, data2);
+ THORSANVIL_SERIALIZE_JsonAttribute(MyClass, data3);
+ typedef boost::mpl::vector<data1, data2, data3> SerializeInfo;
+ };
+ }}}
+
+ int main()
+ {
+ MyClass obj(56, 23.456, "Hi there");
+ std::cout << obj << "\n";
+ std::cout << ThorsAnvil::Serialize::jsonExport(obj) << "\n";
+
+ std::cin >> ThorsAnvil::Serialize::jsonImport(obj);
+ std::cout << obj << "\n";
+ }
+
+

0 comments on commit 68852d4

Please sign in to comment.
Something went wrong with that request. Please try again.