blob: 5ba377ddd2c2cf0c0b37fbda88c0a6c99d0a7964 [file] [log] [blame]
---
title: Serializing Data with the DataSerializable Interface
---
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
The C++ client API provides a `DataSerializable` interface that you can use for fast and compact
data serialization.
<%=vars.product_name%> Portable Data eXchange (PDX) serialization is the recommended option,
but the `DataSerializable` interface can be a good option performance-wise if the size of your objects
is small.
This section discusses the <%=vars.product_name%> `DataSerializable` interface, and
presents implementation examples.
## <a id="concept_696AB5206C3E45898CC1A24CDD93D003__section_8143F965A8C6495E8AB104FD36DA366A" class="no-quick-link"></a>How Serialization Works
When your application puts an object into the cache for subsequent distribution, <%=vars.product_name%> serializes the data by taking these steps:
1. Calls the appropriate `classId` function.
2. Writes the full `typeId` using the `classId` for the instance.
3. Invokes the instances `toData` function.
When your application subsequently receives a byte array, <%=vars.product_name%> takes the following steps:
1. Decodes the `typeId`, extracts the `classId` from the `typeId`, then creates an object of the designated type using the registered factory functions.
2. Invokes the `fromData` function with input from the data stream.
3. Decodes the data, then populates the data fields.
## <a id="concept_696AB5206C3E45898CC1A24CDD93D003__section_786CF85FD80E4FE391135460E04D46CC" class="no-quick-link"></a>Implementing the DataSerializable Interface
To store your own data types in the cache, you need to derive a new subclass from the `DataSerializable`
interface. In practical terms, this means that you need to implement a small set of helper
functions:
1. Write a `toData` function that serializes your data.
``` pre
void toData (DataOutput& output)
```
The `toData` function is responsible for copying all of the objects data fields to the object stream.
The `DataOutput` class represents the output stream and provides methods for writing the primitives in a network byte order.
2. Write a `fromData` function that consumes a data input stream and repopulates the objects data fields.
``` pre
void fromData (DataInput& input)
```
The `DataInput` class represents the input stream and provides methods for reading input
elements. The `fromData` function must read the elements of the input stream in the same order
that they were written by `toData`.
## Example 1. The Simple Class BankAccount
This example demonstrates a simple `BankAccount` class that encapsulates two `ints`, `ownerId` and `accountId`:
``` pre
class BankAccount
{
private:
 
int m_ownerId;
int m_accountId;
 
public:
 
BankAccount( int owner, int account ): m_ownerId( owner ),
m_accountId( account ) {}
 
int getOwner( )
{
return m_ownerId;
}
 
int getAccount( )
{
return m_accountId;
}
 
};
```
To make `BankAccount` serializable, you would need to derive the class from `DataSerializable` and implement the following:
- `toData` a function to serialize the data.
- `fromData` a function to deserialize the data.
- `getClassId` a function to provide a unique integer for the class.
- `TypeFactoryMethod` a pointer to a function that returns a `DataSerializable*` to an uninitialized instance of the type.
## Example 2. Implementing a DataSerializable Class
This example shows a code sample that demonstrates how to implement a serializable class.
``` pre
class BankAccount : public DataSerializable
{
private:
int m_ownerId;
int m_accountId;
public:
BankAccount( int owner, int account ) : m_ownerId( owner ),
m_accountId( account ) {}
int getOwner( )
{
return m_ownerId;
}
int getAccount( )
{
return m_accountId;
}
// Add the following for the DataSerializable interface
// Our TypeFactoryMethod
static DataSerializable* createInstance( )
{
return new BankAccount( 0, 0 );
}
int32_t getClassId( )
{
return 10; // must be unique per class.
}
void toData( DataOutput& output )
{
output.writeInt( m_ownerId );
output.writeInt( m_accountId );
}
DataSerializable* fromData( DataInput& input )
{
input.readInt( &m_ownerId );
input.readInt( &m_accountId );
return this;
}
};
```
## <a id="concept_696AB5206C3E45898CC1A24CDD93D003__section_108942E549CE4DE68FF3956712DEC7AF" class="no-quick-link"></a>Registering the Type
To be able to use the `BankAccount` type, you must register it with the type system so that when an
incoming stream contains a `BankAccount`, it can be manufactured from the associated
`TypeFactoryMethod`.
``` pre
DataSerializable::registerType( BankAccount::createInstance );
```
Typically, you would register the type before calling the function `DistributedSystem::connect`.
**Note:**
Type IDs must be unique to only one class.
## <a id="concept_696AB5206C3E45898CC1A24CDD93D003__section_311C3661023C46328B406F26F4F16808" class="no-quick-link"></a>Custom Key Types
If your application uses key types that are too complex to easily force into `CacheableString`, you
can likely improve performance by deriving a new class from `CacheableKey`. If you have hybrid data
types you can implement your own derivation of `CacheableKey` that encapsulates the data type.
See below for information about implementing key types for a client that is used with a Java cache server.
To extend a `DataSerializable` class to be a `CacheableKey`, you need to modify the class definition as follows:
- Change the class so that it derives from `CacheableKey` rather than `DataSerializable`.
- Implement `operator==` and `hashcode` functions.
## Example 3. Extending a DataSerializable Class To Be a CacheableKey
This example shows how to extend a serializable class to be a cacheable key.
``` pre
class BankAccount
: public CacheableKey
{
private:
int m_ownerId;
int m_accountId;
public:
BankAccount( int owner, int account ) : m_ownerId( owner ),
m_accountId( account ) {}
int getOwner( )
{
return m_ownerId;
}
int getAccount( )
{
return m_accountId;
}
// Our TypeFactoryMethod
static DataSerializable* createInstance( )
{
return new BankAccount( 0, 0 );
}
int32_t typeId( )
{
return 1000; // must be unique per class.
}
void toData( DataOutput& output )
{
output.writeInt( m_ownerId );
output.writeInt( m_accountId );
}
DataSerializable* fromData( DataInput& input )
{
input.readInt( &m_ownerId );
input.readInt( &m_accountId );
return this;
}
// Add the following for the CacheableKey interface
bool operator == ( const CacheableKey& other ) const
{
const BankAccount& otherBA =
static_cast<const BankAccount&>( other );
return (m_ownerId == otherBA.m_ownerId) && (m_accountId == otherBA.m_accountId);
}
uint32_t hashcode( ) const
{
return m_ownerId;
}
virtual int32_t classId( )const
{
return 10; // must be unique per class.
}
 
virtual size_t objectSize() const
{
return 10;
}
};
```
## <a id="concept_696AB5206C3E45898CC1A24CDD93D003__section_AFB685227E4048BF9FB4FD7C55AED274" class="no-quick-link"></a>Serialization in Native Client Mode with a Java Server
Primitive object types supported in all languages (`CacheableInt32`, `CacheableString`,
`CacheableBytes`) function without requiring custom definitions with the Java cache server. For the
keys, the Java cache server has to deserialize them and locate the hashcode to be able to insert the
internal maps. Because of this, key types for C++ clients used with a Java server are required to be
registered on the Java server, but the value types do not need to be registered. This needs to be
done even if there are no Java clients.