2. Type Mappings
This document explains how different types are mapped in each language.
Java
In Java, types are mapped as:
| Type Name | Java-class |
|---|---|
| 32-bit integer | java.lang.Integer, int |
| 64-bit integer | java.lang.Long, long |
| 32-bit float | java.lang.Float, float |
| 64-bit float | java.lang.Double, double |
| String | java.lang.String |
| Boolean | java.lang.Boolean |
| Big Integer | java.math.BigInteger |
| Big Decimal | java.math.BigDecimal |
| Zoned Date Time | java.time.ZonedDateTime |
| Local Date Time | java.time.LocalDateTime |
| Local Date | java.time.LocalDate |
| Local Time | java.time.LocalTime |
| Byte Array | java.lang.byte[] |
| Array | java.util.List / java.util.ArrayList |
| Set | java.util.Set / java.util.HashSet |
| Map | java.util.Map / java.util.HashMap |
JavaScript
Because JavaScript is more restricted in its type system, some quirks and workarounds are needed to make everything work.
Basic Types
Most basic types are mapped as follows:
| Type Name | Actual Type |
|---|---|
| 32-bit integer | number |
| 64-bit integer | number |
| 32-bit float | number |
| 64-bit float | number |
| String | string |
| Boolean | boolean |
| Big Integer | bigint |
| Big Decimal | Custom Encoder needed |
| Zoned Date Time | Temporal.ZonedDateTime |
| Local Date Time | Temporal.PlainDateTime |
| Local Date | Temporal.PlainDate |
| Local Time | Temporal.PlainTime |
| Byte Array | Uint8Array |
| Array | array |
| Set | Set |
| Map | Map |
Number Type Notes
Because JavaScript natively supports only 64-bit floats, other number types require conversions at runtime, which creates some special cases. For this, underflow and overflow strategies are needed. The possibilities are:
Throwing an error (the default)
In this strategy, an error is simply thrown during serialization or deserialization.
Number Capping
In this strategy, overflowing and underflowing numbers are capped to their minimum and maximum safe values. For instance, for 64-bit integers, the maximum safe size is actually 56 bits.
This strategy can be configured as:
const cbot = Cbot.getInstance({
numberOverflowStrategy: {
onDeserialize: OnDeserializeOverflow.CAP_TO_BOUNDS,
onSerialize: OnSerializeOverflow.CAP_TO_BOUNDS
}
});
Not A Number
For deserialization, there is also the option to simply use NaN to specify underflowing and overflowing situations. This can be configured as:
const cbot = Cbot.getInstance({
numberOverflowStrategy: {
onDeserialize: OnDeserializeOverflow.RETURN_NAN
}
});
Temporal API Support
Because the JavaScript Temporal API is finally emerging, the implementation also uses it as the default option. However, because the support is still experimental and requires the use of polyfills, a few considerations are needed.
Configuration
In order to use the Temporal API, it needs to be enabled as:
const cbot = Cbot.getInstance({
temporalApiEnabled: true
})
If the Temporal API is not available, an error will be thrown.
Using Polyfills
The Temporal API has been tested (at the time of writing) with package temporal-polyfill (0.3.2) and works as expected.
In this package, the library is imported as:
import 'temporal-polyfill/global';
Note that the import order matters. The Temporal polyfill must always be imported first, or you will get weird errors. For instance, if you get an error like this:
Inconsistent Meta-class declaration detected for type 'PlainDate'. Did you use 'A'-type without Meta-class declaration as value type?
then the import happened too late and must be done earlier.
Binary Arrays
In JavaScript, the Uint8Array type is considered the binary type. If binary support is needed, special consideration is required when creating a Cbot instance, as JavaScript does not have a universal Base64 converter. Instead, each environment has its own implementations.
Configuration
For instance, in a Node environment, the Cbot instance must be configured as:
const cbot = Cbot.getInstance({
typedArrayCoders: {
uint8ArrayCoder: {
encode: (value:Uint8Array) => Buffer.from(value).toString('base64'),
decode: (value:string) => Uint8Array.from(Buffer.from(value, 'base64'))
}
}
});
In a browser, it can be configured as:
const cbot = Cbot.getInstance({
typedArrayCoders: {
uint8ArrayCoder: {
encode: (value: Uint8Array) => {
let binaryStr = "";
for (let i = 0; i < value.length; i++) {
binaryStr += String.fromCharCode(value[i]);
}
return btoa(binaryStr);
},
decode: (value: string) => {
const binary = atob(value);
const len = binary.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binary.charCodeAt(i);
}
return bytes;
}
}
}
});
Python
Python has quite a complete set of different types, although numeric types require some extra attention.
Basic Types
Most basic types are mapped as follows:
| Type Name | Actual Type |
|---|---|
| 32-bit integer | int |
| 64-bit integer | int |
| 32-bit float | float |
| 64-bit float | float |
| String | str |
| Boolean | bool |
| Big Integer | int |
| Big Decimal | Decimal |
| Zoned Date Time | datetime (with zone-info) |
| Local Date Time | datetime (without zone-info) |
| Local Date | date |
| Local Time | time |
| Byte Array | bytearray |
| Array | list |
| Set | set |
| Map | Map (Custom dictionary) |
Number Type Notes
Because Python has only a big integer type for all integers, the Cbot instance can be configured in three different ways regarding how it serializes integers in the untyped mode. The possibilities are:
- 64-bit integers only: In this mode, all numbers that are beyond the 64-bit range are rejected. This may be important in cases where the other party expects only numbers of this type
- 64-bit integers promoted: This is the default setting. In this mode, integers are sent as 64-bit numbers but are promoted to big integers if they do not fit within the constraints. Current implementations try to coerce the given integer to the type it fits, but it may fail at the receiving end
- Big integer only: In this mode, all integers are sent as big integers
This mode is configured via CbotOptions.
Map Notes
In Python, the default "map" is a dictionary. Unfortunately, it has a double role as an anonymous object, which is common when parsing JSON. For instance, in Java and JavaScript, both cases have well-defined counterparts. Thus, for Python, a Map class was created to specify that it is indeed a map and not an anonymous object.