Using Automatic PDX Serialization

You can allow your C++ client applications to automatically PDX serialize and deserialize domain objects without having to add any extra code by using the provided pdxautoserializer command line tool.

When using the C++ client API, you can automatically serialize and deserialize domain objects without making any code changes to those objects or having to implement a PdxSerializer or PdxSerializable interface and their related fromData and toData methods. The pdxautoserializer command-line utility allows you to generate C++ code that will serialize your domain objects in the PDX format for you.

How to Use Automatic PDX Serialization

The procedure below uses the following sample class:

class PortfolioPdx 
{
  private:
    int32_t id;
    char* pkid;
    PositionPdxPtr position1;
    PositionPdxPtr position2;
    CacheableHashMapPtr positions;
    char** names;    
    int8_t* newVal;
    CacheableDatePtr creationDate;
    int8_t* arrayNull;
    int8_t* arrayZeroSize;
  public:
    // CTOR
    // DTORS
    // Other Methods declarations

For each domain class you provide, all fields are considered for serialization except those defined as static or transient and those you explicitly exclude using macros.

  1. Inherit your class from apache::geode::client::PdxSerializable.

    class PortfolioPdx : public PdxSerializable
    
  2. Add the following method declarations in the public part of the class.

    const char* getClassName() const;
    virtual void toData(apache::geode::client::PdxWriterPtr pw);
    virtual void fromData(apache::geode::client::PdxReaderPtr pr);
    static PdxSerializable* createDeserializable();
    
  3. In your pre-build environment (for example in your makefiles), call pdxautoserializer as follows:

    <GFCPP>/bin/pdxautoserializer.exe --outDir=<location to generate files> <SOURCE_DIR>/PortfolioPdx.hpp
    
  4. Include the generated file in your project and compile.

The following is an example of a generated file:

#include "PortfolioPdx.hpp"
#include <geode/PdxWriter.hpp>
#include <geode/PdxReader.hpp>
#include <geode/PdxAutoSerializer.hpp>
namespace testobject
{
  void PortfolioPdx::toData(apache::geode::client::PdxWriterPtr var)
  {
    apache::geode::client::PdxAutoSerializable::writePdxObject(var, "id", id);
    apache::geode::client::PdxAutoSerializable::writePdxObject(var, "pkid", pkid);
    apache::geode::client::PdxAutoSerializable::writePdxObject(var, "position1", position1);
    apache::geode::client::PdxAutoSerializable::writePdxObject(var, "position2", position2);
    apache::geode::client::PdxAutoSerializable::writePdxObject(var, "positions", positions);
    apache::geode::client::PdxAutoSerializable::writePdxObject(var, "status", status);
    apache::geode::client::PdxAutoSerializable::writePdxObject(var, "creationDate", creationDate);
  }

  void PortfolioPdx::fromData(PdxReaderPtr var)
  {
    apache::geode::client::PdxAutoSerializable::readPdxObject(var, "id", id);
    apache::geode::client::PdxAutoSerializable::readPdxObject(var, "pkid", pkid);
    apache::geode::client::PdxAutoSerializable::readPdxObject(var, "position1", position1);
    apache::geode::client::PdxAutoSerializable::readPdxObject(var, "position2", position2);
    apache::geode::client::PdxAutoSerializable::readPdxObject(var, "positions", positions);
    apache::geode::client::PdxAutoSerializable::readPdxObject(var, "status", status);
    apache::geode::client::PdxAutoSerializable::readPdxObject(var, "creationDate", creationDate);
  }

  const char* PortfolioPdx::getClassName()  const
  {
     return "PortfolioPdx";
  }
}

Handling Arrays

  1. Define the following macro in your header file:

    #define GFARRAYSIZE(x)
    
  2. Assuming that the following is the class member of type array:

    int8_t* newVal;
    

    Then define a new variable which sets the length of the array:

    int32_t newValSize;
    
  3. Tag the new variable with the GFARRAYSIZE macro as follows:

    GFARRAYSIZE(newVal) int32_t newValSize;
    

Using a Single Variable as Length for Multiple Arrays

You can use the GFARRAYSIZES to have single length for multiple arrays.

Define the GFARRAYSIZES macro as follows:

#define GFARRAYSIZES(x)

The following is an example usage:

class ArrayOfSizes?
    {
    public:
    int32_t* array1;
    int32_t* array2;
    int32_t* array3;
    int32_t* array4;
    int32_t* array5;

    GFARRAYSIZE(array1) int32_t singleSize; 
    GFARRAYSIZES("array2,array3,array4,array5") int32_t SingleSizeToMultipleArrays?;
  };​

Excluding Member Variables from Serialization

  1. Define the following macro in your header file:

    #define GFEXCLUDE
    
  2. Tag your member variable with this macro:

    GFEXCLUDE char* type;
    

Marking Identity Fields

Identity fields are used when comparing objects using the hashCode and equals methods.

  1. Define the following macro in your header file.

    #define GFID(x)
    
  2. Assuming that the following is the class member you want to use as IdentityField:

    int8_t* newVal;
    

    Tag the member with the GFID macro as follows:

    GFID(newVal)int8_t* newVal;
    

Ignoring User Defined Keywords

You might have certain user defined keywords after the class name. Current C++ grammar does not support this. If you have some keywords user will have to ignore them by using the GFIGNORE macro.

For example, consider the following class definition:

#ifdef _WIN32
#ifdef BUILD_TESTOBJECT
#define TESTOBJECT_EXPORT _GEODE_LIBEXP
#else
#define TESTOBJECT_EXPORT _GEODE_LIBIMP
#endif
#else
#define TESTOBJECT_EXPORT
#endif

namespace PdxAutoTests {
  class TESTOBJECT_EXPORT PdxAutoMegaType :  public PdxSerializable {
  }

Currently, the pdxautoserializer tool will fail to recognize TESTOBJECT_EXPORT. Change your class by adding the GFIGNORE macro as follows:

#ifdef _WIN32
#ifdef BUILD_TESTOBJECT
#define TESTOBJECT_EXPORT _GEODE_LIBEXP
#else
#define TESTOBJECT_EXPORT _GEODE_LIBIMP
#endif
#else
#define TESTOBJECT_EXPORT
#endif

using namespace apache::geode::client;

#define GFIGNORE(X) X
#define GFID

namespace PdxAutoTests {
  class GFIGNORE(TESTOBJECT_EXPORT) PdxAutoMegaType :  public PdxSerializable {

Additional Usage Information for the pdxautoserializer Tool

The pdxautoserializer tool takes classes as input and generates code that will serialize the class into the PDX format for you.

The pdxautoserializer tool is located in $GEODE/bin where $GEODE corresponds to the installation location of the client.

Some additional notes about using the pdxautoserializer tool:

  • Any const type in the class members are ignored by the pdxserializer tool.
  • Generated files will have namespace in the file name.

To view the command-line help for the tool, type:

prompt> pdxautoserializer.exe --help

Help returns the following syntax and usage information:

Usage: pdxautoserializer.exe [OPTIONS] <resources e.g. header> ...

Resource name should be the path to the header containing the classes to be 
auto-serialized.

[OPTIONS] may be one of those given below.

SINGLE denotes that the option should be specified only once.
MULTIPLE denotes that the option can be specified more than once.
OPTIONAL denotes that the option may be skipped in which case the default 
for that shall be chosen.

--className=VALUE       Name of the class for which to generate auto-serialization code (MULTIPLE,OPTIONAL)
--classNameStr=VALUE    Name of the class in string (MULTIPLE,OPTIONAL)
--help                  This help message.
--outDir                The output directory of the generated files (SINGLE,OPTIONAL)
--suffix                The suffix of the generated filenames -- default is 'Serializable' (SINGLE,OPTIONAL)
--usage                 This usage message.

Examples:
pdxautoserializer -outDir=<DIR NAME> <RESOURCE>
pdxautoserializer -outDir=<DIR NAME> --className=<CLASSNAME1> --className=<CLASSNAME2> <RESOURCE>
pdxautoserializer -outDir=<DIR NAME> --classNameStr=<CLASSNAME1:User defined String> --classNameStr=<CLASSNAME:User defined String> <RESOURCE>

Helper Macros to be defined in Input Header File :
GFINCLUDE        for including a specific member for serialization
GFEXCLUDE        for excluding a specific member for serialization
GFID             for considering a member as Identify Field
GFARRAYSIZE      for specifying a array length member
GFIGNORE         for ignoring certain keywords
For more details refer to documentation on this utility.

Generating Automatic Code for a Single Class

Many times there are multiple classes in a single header file. For example:

#ifndef HEADER_HEADER
#define HEADER_HEADER

class class1{
};
class class2{
};
class class3 : public PdxSerializable{
};
#endif

If you want to generate code for only one of the classes, then use the --className option. For example, if you only want to generate code for class3, then you would use the following command:

pdxautoserializer --outDir=<outDir> --className=class3

Choosing Your Own Suffix to Identify the Generated Files.

The pdxserializer tool also provides the option to choose your own suffix for the generated C++ files. This can help you write less code in your makefiles. Here’s an example command:

pdxautoserializer --outDir=<outDir> --className=CharTypes --suffix="generated"

Example of Using PDX Serialization in Your Application

CacheFactoryPtr cacheFactory = CacheFactory::createCacheFactory();
    // Create a cache with the "clientPdxRemoteQuery.xml" Cache XML file.
    CachePtr cachePtr = cacheFactory->set("cache-xml-file", "XMLs\\clientPdxRemoteQuery.xml")
                        ->create();

    LOGINFO("Created the Cache");

    // Get the example Region from the Cache which is declared in the Cache XML file.
    RegionPtr regionPtr = cachePtr->getRegion( "Portfolios");

    LOGINFO( "Obtained the Region from the Cache");

    // Register our Serializable/Cacheable Query objects, viz. PortfolioPdx and PositionPdx.
    Serializable::registerPdxType(PortfolioPdx::createDeserializable);
    PortfolioPdxPtr port1Ptr(new PortfolioPdx(1 /*ID*/, 10 /*size*/));
    PortfolioPdxPtr port2Ptr(new PortfolioPdx(2 /*ID*/, 20 /*size*/));
    PortfolioPdxPtr port3Ptr(new PortfolioPdx(3 /*ID*/, 30 /*size*/));
    regionPtr->put("Key1", port1Ptr);
    regionPtr->put("Key2", port2Ptr);
    regionPtr->put("Key3", port3Ptr);

    // Get the QueryService from the Cache.
    QueryServicePtr qrySvcPtr = cachePtr->getQueryService( "examplePool");

    LOGINFO( "Got the QueryService from the Cache");

    // Execute a Query which returns a ResultSet.
    QueryPtr qryPtr = qrySvcPtr->newQuery("SELECT DISTINCT * FROM /Portfolios");
    SelectResultsPtr resultsPtr = qryPtr->execute();

    LOGINFO( "ResultSet Query returned %d rows", resultsPtr->size());