14.3. Serialization Utilities¶
Fast DDS provides methods to serialize XTypes objects, enabling efficient data exchange and manipulation within distributed systems. Serialization is a crucial process in data distribution services, as it converts complex data structures into a format that can be easily transmitted and reconstructed across different platforms and programming environments.
14.3.1. Dynamic Type to IDL¶
The method idl_serialize serializes a DynamicType object to its IDL representation.
Note
The conversion to IDL only supports the following builtin annotations:
@bit_bound, @extensibility, @key, and @position.
Warning
The conversion to IDL of a Bitset with inheritance merges derived Bitsets with their base Bitset.
Warning
The conversion to IDL dismisses values explicitly set to their default value.
For example, the default @bit_bound value of a Bitmask is 32.
If a user were to explicitly set the @bit_bound value of a
Bitmask to 32 and then serialize the DynamicType to IDL, the
@bit_bound would not be included in the IDL.
14.3.1.1. Example: Convert a discovered type to IDL format¶
The following example demonstrates how to use the idl_serialize method in Fast DDS to convert
discovered types to IDL format.
Each time the subscriber discovers a new DataReader or DataWriter, it uses the
DynamicTypeBuilderFactory to build a DynamicType and serialize it to IDL format.
Please refer to Remote type discovery and endpoint matching section for more details on how to implement
remote type discovery.
/* Custom Callback on_data_reader_discovery */
void on_data_reader_discovery(
DomainParticipant* /* participant */,
eprosima::fastdds::rtps::ReaderDiscoveryStatus /* reason */,
const eprosima::fastdds::dds::SubscriptionBuiltinTopicData& info,
bool& /* should_be_ignored */) override
{
// Get remote type information
xtypes::TypeObject remote_type_object;
if (RETCODE_OK != DomainParticipantFactory::get_instance()->type_object_registry().get_type_object(
info.type_information.type_information.complete().typeid_with_size().type_id(),
remote_type_object))
{
// Error
return;
}
// Build remotely discovered type
DynamicType::_ref_type remote_type = DynamicTypeBuilderFactory::get_instance()->create_type_w_type_object(
remote_type_object)->build();
// Serialize DynamicType into its IDL representation
std::stringstream idl;
if (RETCODE_OK != idl_serialize(remote_type, idl))
{
// Error
return;
}
// Print IDL representation
std::cout << "Type discovered:\n" << idl.str() << std::endl;
}
/* Custom Callback on_data_writer_discovery */
void on_data_writer_discovery(
DomainParticipant* /* participant */,
eprosima::fastdds::rtps::WriterDiscoveryStatus /*reason*/,
const eprosima::fastdds::dds::PublicationBuiltinTopicData& info,
bool& /* should_be_ignored */) override
{
// Get remote type information
xtypes::TypeObject remote_type_object;
if (RETCODE_OK != DomainParticipantFactory::get_instance()->type_object_registry().get_type_object(
info.type_information.type_information.complete().typeid_with_size().type_id(),
remote_type_object))
{
// Error
return;
}
// Build remotely discovered type
DynamicType::_ref_type remote_type = DynamicTypeBuilderFactory::get_instance()->create_type_w_type_object(
remote_type_object)->build();
// Serialize DynamicType into its IDL representation
std::stringstream idl;
if (RETCODE_OK != idl_serialize(remote_type, idl))
{
// Error
return;
}
// Print IDL representation
std::cout << "Type discovered:\n" << idl.str() << std::endl;
}
14.3.2. DynamicData to JSON¶
In the context of DDS (Data Distribution Service), DynamicType represents the structure of the data being
distributed across the system.
Each DynamicData object corresponds to an object of the type represented by its DynamicType,
providing functionalities to access and modify data values.
To enhance interoperability and readability, it is often useful to serialize DynamicData into a more
manageable format, to enable easier data processing and analysis across different systems and applications.
The method json_serialize converts a DynamicData object into a JSON object, then
dumped into a std::ostream.
The inverse conversion is also possible using the method json_deserialize, as described in the
corresponding section.
14.3.2.1. Supported Types¶
This section provides the serialization of DynamicData to JSON ostream for all supported types.
14.3.2.1.1. Primitives¶
Primitive types are the basic building blocks for every DynamicType.
Below is an example of the definition of primitive types in IDL:
struct PrimitivesStruct
{
boolean my_bool;
octet my_octet;
char my_char;
wchar my_wchar;
long my_long;
unsigned long my_ulong;
int8 my_int8;
uint8 my_uint8;
short my_short;
unsigned short my_ushort;
long long my_longlong;
unsigned long long my_ulonglong;
float my_float;
double my_double;
long double my_longdouble;
};
A DynamicData object corresponding to the type represented above
would be serialized as follows:
{
"my_bool": false,
"my_octet": 0,
"my_char": "\u0000",
"my_wchar": "\u0000",
"my_long": 0,
"my_ulong": 0,
"my_int8": 0,
"my_uint8": 0,
"my_short": 0,
"my_ushort": 0,
"my_longlong": 0,
"my_ulonglong": 0,
"my_float": 0.0,
"my_double": 0.0,
"my_longdouble": 0.0
}
14.3.2.1.2. Strings¶
String types are used to represent sequences of characters, which are essential for handling textual data within the system. The following example shows the definition of string types in IDL:
struct StringsStruct
{
string my_string;
wstring my_wstring;
string<41925> my_bounded_string;
wstring<20925> my_bounded_wstring;
};
A DynamicData object corresponding to the type represented above
would be serialized as follows:
{
"my_string": "",
"my_wstring": "",
"my_bounded_string": "",
"my_bounded_wstring": ""
}
14.3.2.1.3. Enumerations¶
Enumeration types represent a fixed set of named values, making it easier to work with a predefined list of options. Below is an example of the definition of enumeration types in IDL:
enum MyEnum
{
A,
B,
C
};
struct EnumStruct
{
MyEnum my_enum;
};
The previous DynamicData object can be serialized in two different formats, eProsima and OMG, providing
flexibility depending on the required interoperability and compatibility with other systems.
The previous DynamicData object would be serialized as follows in the different formats:
{
"my_enum": {
"name": "A",
"value": 0
}
}
{
"my_enum": "A"
}
14.3.2.1.4. Bitmasks¶
Bitmask types allow the representation of a set of flags in a single value, which can be useful for storing multiple boolean options compactly. Here is an example of the definition of bitmask types in IDL:
@bit_bound(8)
bitmask MyBitMask
{
@position(0) flag0,
flag1,
flag2,
@position(5) flag5
};
struct BitmaskStruct
{
MyBitMask my_bitmask;
};
Bitmask also present different serialized structures in the different formats eProsima and OMG.
The previous DynamicData object would be serialized as follows in the different formats:
{
"my_bitmask": {
"active": [],
"binary": "00000000",
"value": 0
}
}
{
"my_bitmask": 0
}
14.3.2.1.5. Sequences¶
Sequence types are used to represent ordered collections of elements, similar to arrays but with dynamic length. Below is an example of the definition of sequence types in IDL:
struct SequenceStruct
{
sequence<MyBitMask> bitmask_sequence;
sequence<short, 5> short_sequence;
};
A DynamicData object corresponding to the type represented above
would be serialized as follows:
{
"bitmask_sequence": [],
"short_sequence": []
}
14.3.2.1.6. Arrays¶
Array types are used to represent fixed-size collections of elements. The following example shows the definition of array types in IDL:
struct ArrayStruct
{
long long_array[2][3];
};
A DynamicData object corresponding to the type represented above
would be serialized as follows:
{
"long_array": [
[
0,
0,
0
],
[
0,
0,
0
]
]
}
14.3.2.1.7. Maps¶
Map types represent collections of key-value pairs, allowing for the efficient lookup of values based on unique keys. Below is an example of the definition of map types in IDL:
struct MapStruct
{
map<string, MyAliasedBoundedString> string_alias_unbounded_map;
map<short, long, 2> short_long_map;
};
A DynamicData object corresponding to the type represented above
would be serialized as follows:
{
"short_long_map": null,
"string_alias_unbounded_map": null
}
14.3.2.1.8. Structures¶
Structure types are used to group different types of data together. Here is an example of the definition of structure types in IDL:
struct InnerStruct
{
@id(0x10) long first;
};
struct ParentStruct
{
float first;
long long second;
};
struct ComplexStruct : ParentStruct
{
InnerStruct complex_member;
};
A DynamicData object corresponding to the type represented above
would be serialized as follows:
{
"complex_member": {
"first": 0
},
"first": 0.0,
"second": 0
}
14.3.2.1.9. Unions¶
Union types are a special type of structure type where only one member exists. Below is an example of the definition of union types in IDL:
union InnerUnion switch (short)
{
case 0:
@id(0x10) PrimitivesStruct first;
case 1:
default:
long long second;
};
union ComplexUnion switch (long)
{
case 0:
case 1:
long third;
default:
InnerUnion fourth;
};
A DynamicData object corresponding to the type represented above
would be serialized as follows:
{
"inner_union": {
"second": 0
},
"complex_union": {
"fourth": {
"second": 0
}
}
}
14.3.2.1.10. Bitsets¶
Bitset types are a specialized type of structure where individual bits can be accessed and manipulated. Below is an example of the definition of bitset types in IDL:
bitset MyBitSet
{
bitfield<3> a;
bitfield<1> b;
bitfield<4>;
bitfield<10> c;
bitfield<12, short> d;
};
struct BitsetStruct
{
MyBitSet my_bitset;
};
A DynamicData object corresponding to the type represented above
would be serialized as follows:
{
"my_bitset": {
"a": 0,
"b": 0,
"c": 0,
"d": 0
}
}
14.3.2.2. Example: Convert received data into JSON format¶
The following code demonstrates how to use the json_serialize function in Fast DDS to serialize
received data into a more manageable and understandable JSON format.
Each time the subscriber receives new data, the corresponding DynamicData can be obtained from the
DynamicDataFactory and serialized into a JSON string format.
Please refer to Remote type discovery and endpoint matching section for more details on how to implement
remote type discovery.
void on_data_available(
DataReader* reader)
{
// Create data using the DynamicType created from discovered TypeObject or through Dynamic Language Binding API
DynamicData::_ref_type new_data =
DynamicDataFactory::get_instance()->create_data(dyn_type_serialization_);
SampleInfo info;
while ((RETCODE_OK == reader->take_next_sample(&new_data, &info)))
{
std::stringstream output;
output << std::setw(4);
// Serialize DynamicData into JSON string format
if (RETCODE_OK != json_serialize(
new_data,
DynamicDataJsonFormat::EPROSIMA,
output))
{
// Error
return;
}
// Print JSON representation
std::cout << "Message received:\n" << output.str() << std::endl;
}
}
// DynamicType corresponding to received data
DynamicType::_ref_type dyn_type_serialization_;
14.3.3. JSON to DynamicData¶
Apart from having the possibility to serialize DynamicData to JSON,
Fast DDS also provides a way to perform the inverse conversion.
The method json_deserialize is able to convert a JSON object into a DynamicData instance,
by only providing its associated DynamicType.
This method is useful for injecting data from external sources into a DDS network, allowing for the integration of data
from various systems and applications.
14.3.3.1. Example: Convert JSON data into DynamicData¶
The following code demonstrates how to use the json_deserialize function in Fast DDS to convert
JSON data into a DynamicData object.
void json_to_data(
const std::string& json_data)
{
// Deserialize JSON string into DynamicData
// The required DynamicType can be created from discovered TypeObject or through Dynamic Language Binding API
DynamicData::_ref_type new_data;
if (RETCODE_OK != json_deserialize(
json_data,
dyn_type_deserialization_,
DynamicDataJsonFormat::EPROSIMA,
new_data))
{
// Error
return;
}
// Process the new data
}
// DynamicType corresponding to JSON data to be deserialized into DynamicData
DynamicType::_ref_type dyn_type_deserialization_;