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;
idl_serialize(remote_type, idl);
// 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;
idl_serialize(remote_type, idl);
// 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
.
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)
{
// Dynamic DataType
DynamicData::_ref_type new_data =
DynamicDataFactory::get_instance()->create_data(dyn_type_);
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
json_serialize(new_data, DynamicDataJsonFormat::EPROSIMA, output);
std::cout << "Message received:\n" << output.str() << std::endl;
}
}
// DynamicType created in discovery callback
DynamicType::_ref_type dyn_type_;