Serialization
Most simple types, such as numbers, booleans, strings, arrays (lists) and promises (tasks) of them are marshalled in-memory when crossing the C# <-> JavaScript boundary. Below are some of the natively-supported types (refer to .NET docs for the full list):
C# | JavaScript | Task of | Array of |
---|---|---|---|
bool | boolean | ✔️ | ❌ |
byte | number | ✔️ | ✔️ |
char | string | ✔️ | ❌ |
string | string | ✔️ | ✔️ |
int | number | ✔️ | ✔️ |
long | BigInt | ✔️ | ❌ |
float | Number | ✔️ | ❌ |
DateTime | Date | ✔️ | ❌ |
When a value of non-natively supported type is specified in an interop API, Bootsharp will attempt to de-/serialize it with System.Text.JSON using fast source-generation mode. The whole process is encapsulated under the hood on both the C# and JavaScript sides, so you don't have to manually author generator hints or specify [MarshallAs]
attributes for each value:
public record User (long Id, string Name, DateTime Registered);
[JSInvokable]
public static void AddUser (User user) { }
[JSEvent]
public static partial void OnUserModified (User user);
— Bootsharp will automatically emit C# and JavaScript code required to de-/serialize User
record on both ends, so that you can consume the APIs as if they were initially authored in JavaScript:
import { Program } from "bootsharp";
Program.addUser({ id: 17, name: "Carl", registered: Date.now() });
Program.onUserModified.subscribe(handleUserModified);
function handleUserModified(user: Program.User) { }
Enums Serialization
Enums are marshalled as numbers for better performance, while additional name <-> index mappings are emitted on the JavaScript side for convenience.
public enum Options { Foo, Bar }
[JSInvokable]
public static Options GetOption () => Options.Bar;
— while "GetOptions" return value will be passed to JavaScript as an integer index, Bootsharp will map enum indexes to string values (and vice-versa) in the emitted code, so that following will work as expected:
import { Program } from "bootsharp";
const option = Program.getOption();
console.log(option === Program.Options.Foo); // false
console.log(option === Program.Options.Bar); // true
console.log(Program.Options[Program.Options.Foo]); // "Foo"
console.log(Program.Options[1]); // "Bar"
Dictionary Serialization
ES6 Map doesn't natively support JSON serialization, hence Bootsharp will use plain objects when serializing C# dictionaries:
[JSInvokable]
public static Dictionary<string, bool> GetMap () =>
new () { ["foo"] = true, ["bar"] = false };
— the dictionary can be accessed via keys as usual JavaScript object:
import { Program } from "bootsharp";
const map = Program.getMap();
console.log(map.foo); // true
console.log(map["bar"]); // false
Collection Interfaces
It's common to use various collection interfaces, such as IReadOnlyList
or IReadOnlyDictionary
when authoring C# APIs. Bootsharp will accept any kind of array or dictionary compatible interface in the interop APIs and marshal them as plain arrays and maps by default:
[JSInvokable]
public static IReadOnlyDictionary<string, float> Map (
IReadOnlyList<string> a, IReadOnlyCollection<float> b) { }
import { Program } from "bootsharp";
const map = Program.map(["foo", "bar"], [0, 7]);
console.log(map.bar); // 7
Configuring Serialization Behaviour
To override default JSON serializer options used for marshalling the interop data, use Bootsharp.Serializer.Options
property before the program entry point is invoked:
static class Program
{
static Program () // Static constructor is invoked before 'Main'
{
// Make enums serialize as strings.
var converter = new JsonStringEnumConverter();
Bootsharp.Serializer.Options.Converters.Add(converter);
}
public static void Main () { }
}
Refere to .NET docs for the available serialization options: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/overview.