Skip to main content

CDR Schemas

CDR (Common Data Representation) is the binary serialisation used by DDS and ROS 2. MAPS supports plain XCDR1 samples, including big- and little-endian encoding, through the schema library's CdrFormatter.

The formatter requires a definition of the payload layout. It can compile a supported subset of DDS IDL, an ordered JSON Schema with CDR metadata, or a native ordered CDR field schema. The definition is compiled once when the formatter is created; the same compiled field layout is reused for subsequent messages.


1. Description​

CDR is a schema-driven binary format. The order, width and alignment of fields determine the exact wire representation.

  • Signed and unsigned integer widths of 8, 16, 32 and 64 bits
  • float32, float64, Boolean, 8-bit character and UTF-8 string values
  • Nested structures, fixed-length arrays and variable-length sequences
  • Plain XCDR1 encapsulation headers (CDR_BE and CDR_LE)
  • JSON encoding/decoding, structured field lookups and selector access through ParsedObject

CDR differs from CBC: CDR uses byte alignment and fixed-width primitive encodings, whereas CBC is a bit-packed layout.


2. Schema Definition​

The schema object of a SchemaConfig with format: "cdr" must contain exactly one of:

  1. fields — an ordered native CDR field description.
  2. idl — source text containing a supported IDL definition, with optional typeName.
  3. properties — a JSON Schema object with explicit x-cdr-order and numeric formats.

The optional littleEndian property selects the encoding endianness (default: true). Decoding reads the endianness from the incoming CDR header.

2.1 IDL​

Example Reading.idl:

module telemetry {
typedef unsigned long SensorId;

struct Position {
float latitude;
float longitude;
};

struct Reading {
SensorId sensorId;
Position position;
double temperature;
sequence<short> samples;
};
};

Supply the IDL as a string in schema.idl, and select telemetry::Reading using schema.typeName. If the IDL contains exactly one struct, the type name may be omitted.

The current IDL parser supports structs, modules, typedefs, named type references, primitives, fixed-size arrays and unbounded sequences. It is not a general-purpose OMG IDL compiler. Enums, unions, annotations, bounded sequences and other unsupported declarations are rejected.

2.2 Native ordered CDR schema​

{
"fields": [
{ "name": "sensorId", "type": "uint32" },
{ "name": "temperature", "type": "float64" },
{ "name": "samples", "type": "sequence", "element": "int16" },
{
"name": "location",
"type": "struct",
"fields": [
{ "name": "latitude", "type": "float32" },
{ "name": "longitude", "type": "float32" }
]
}
]
}

Field order is significant. Each field requires name and type. Structures use fields; collections use element, and fixed arrays also require length. Supported type aliases include short/int16, long/int32, longlong/int64, float/float32 and double/float64.

2.3 JSON Schema with explicit CDR layout​

Ordinary JSON Schema does not specify CDR field ordering or integer widths. MAPS therefore requires x-cdr-order at each object level, and explicit format or x-cdr-type for numeric fields.

{
"type": "object",
"x-cdr-order": ["sensorId", "temperature", "samples"],
"properties": {
"samples": {
"type": "array",
"items": { "type": "integer", "format": "int16" }
},
"temperature": { "type": "number", "format": "float64" },
"sensorId": { "type": "integer", "format": "uint32" }
}
}

An array with identical minItems and maxItems is compiled as a fixed array; an array without those limits is a sequence. Bounded variable-length sequences are not yet supported. $ref resolution is not currently implemented.


3. SchemaConfig Example​

CDR schemas use the same registration and lifecycle metadata model as other MAPS formats.

{
"versionId": "1",
"name": "Temperature-CDR",
"description": "Temperature reading encoded using plain XCDR1",
"format": "cdr",
"schema": {
"fields": [
{ "name": "sensorId", "type": "uint32" },
{ "name": "temperature", "type": "float64" },
{ "name": "timestamp", "type": "int64" }
]
}
}

For IDL, replace the schema value with:

{
"idl": "struct Reading { unsigned long sensorId; double temperature; long long timestamp; };",
"typeName": "Reading"
}

The .idl filename is not a separate SchemaConfig format. The caller reads the IDL file and supplies its contents to the CDR configuration.


4. Java Usage​

4.1 Loading an IDL file​

String idl = Files.readString(Path.of("Reading.idl"), StandardCharsets.UTF_8);

CdrSchemaConfig config = new CdrSchemaConfig();
config.setIdl(idl, "telemetry::Reading");

MessageFormatter formatter =
MessageFormatterFactory.getInstance().getFormatter(config);

The example uses java.nio.file.Files, Path and StandardCharsets alongside CdrSchemaConfig, MessageFormatter and MessageFormatterFactory.

4.2 Loading a CDR JSON schema file​

String source = Files.readString(Path.of("Reading.cdr"), StandardCharsets.UTF_8);
JsonObject definition = JsonParser.parseString(source).getAsJsonObject();

CdrSchemaConfig config = new CdrSchemaConfig();
config.setSchema(definition);

MessageFormatter formatter =
MessageFormatterFactory.getInstance().getFormatter(config);

For an extended JSON Schema, use config.setJsonSchema(definition). Both inputs are compiled on formatter creation.

4.3 Encoding and decoding an event​

JsonObject event = new JsonObject();
event.addProperty("sensorId", 7);
event.addProperty("temperature", 21.5);
event.addProperty("timestamp", 1794301200000L);

byte[] payload = formatter.parseFromJson(event);
JsonObject decoded = formatter.parseToJson(payload, ParseMode.STRICT);
ParsedObject parsed = formatter.parse(payload, ParseMode.STRICT);
Object temperature = parsed.get("temperature");

This example uses the three-field Temperature-CDR schema above. parse() exposes structured lookup through the shared ParsedObject API, including nested field paths and array indexes. With ParseMode.IGNORE, a failed parse() falls back to the unparsed byte payload.


5. Wire Representation​

Plain XCDR1 samples begin with a four-byte encapsulation header:

HeaderMeaning
00 00 00 00Plain CDR, big-endian
00 01 00 00Plain CDR, little-endian

Values follow in schema order, with the required CDR alignment. Strings carry a 32-bit length including the terminating NUL; sequences carry a 32-bit element count. Arrays have a fixed element count defined by the schema.

For example, a single uint32 field with the value 305419896 (0x12345678) encodes as:

Little-endian: 00 01 00 00 78 56 34 12
Big-endian: 00 00 00 00 12 34 56 78

6. Limitations and Best Practices​

  • Plain XCDR1 only. XCDR2, parameter-list CDR and IDL unions are not supported by this formatter.
  • Match the exact source schema. Field reordering or changing widths will change the binary layout.
  • Preserve the source IDL or native CDR layout when registering and versioning schemas.
  • The uint64 binary codec and parseToJson() can represent the full unsigned range, but exact uint64 numeric values through ParsedObject and some selector paths remain a known limitation. Do not assume all 64-bit unsigned identifiers remain exact when accessed as untyped Java numbers.
  • This is a payload formatter, not a DDS transport, discovery service or ROS 2 client.

The schemas repository includes JUnit tests and independently sourced ROS 2 and eProsima Fast-CDR golden vectors for supported XCDR1 layouts.