Transit is a data format and a set of libraries for conveying values between applications written in different languages. This library provides support for marshalling Transit data to/from C#.
This implementation's major.minor version number corresponds to the version of the Transit specification it supports.
JSON and JSON-Verbose formats are implemented. MessagePack is not implemented.
Note: This is a modernized fork of rickbeerendonk/transit-csharp, ported to .NET 10 with a focus on performance and modern C# idioms. The original implementation is preserved in
src_old/.
| Original | This fork | |
|---|---|---|
| Target framework | .NET Framework 4.5 / PCL | .NET 10 |
| JSON engine | Newtonsoft.Json | System.Text.Json (Utf8JsonWriter / Utf8JsonReader) |
| Namespace | Beerendonk.Transit |
Transit |
Utf8JsonWriter+ArrayBufferWriter<byte>— the emitter writes directly into a pooled byte buffer; the buffer is then copied to the destinationStreamin one shot and reset. No intermediateMemoryStreamorTextWriterallocation.FrozenDictionary<Type, IWriteHandler>— handler tables are built once at startup and stored in aFrozenDictionaryfor minimal lookup cost.ConcurrentDictionaryhandler cache — types not directly registered (subclasses, interface implementations) are resolved by a one-time walk of the type hierarchy; the result is memoised so subsequent serialisations of the same type are O(1).- Typed fast paths for primitive element sequences (
IEnumerable<int>,IEnumerable<long>,IEnumerable<double>, etc.) avoid boxing in homogeneous arrays. string.Createused inWriteCacheandAbstractEmitter.Escapeto build escaped strings without intermediate allocations.IWriter<T>now implementsIDisposable; theUtf8JsonWriterand (optionally) the underlyingStreamare disposed properly. TheownsStreamparameter controls whether the writer takes ownership of the stream.
Utf8JsonReader(aref struct) — streaming, zero-copy JSON parsing directly over the raw UTF-8 byte buffer returned by the stream.FrozenDictionary<string, IReadHandler>— same pattern as the write side.NullKeyDictionary— a bespokeIDictionaryimplementation that permitsnullkeys, matching JavaHashMapsemantics required for thecmap(composite-map) transit type.- Fast path for
MemoryStreaminputs: avoids a redundantCopyToby accessing the internal buffer segment viaTryGetBuffer.
| Transit type | Write accepts | Read returns |
|---|---|---|
| null | null |
null |
| string | System.String |
System.String |
| boolean | System.Boolean |
System.Boolean |
| integer | byte, short, int, long |
System.Int64 |
| decimal | float, double |
System.Double |
| keyword | Transit.IKeyword |
Transit.IKeyword |
| symbol | Transit.ISymbol |
Transit.ISymbol |
| big decimal | System.Decimal, Transit.Numerics.BigRational |
Transit.Numerics.BigRational |
| big integer | System.Numerics.BigInteger |
System.Numerics.BigInteger |
| time | System.DateTime |
System.DateTime |
| uri | System.Uri |
System.Uri |
| uuid | System.Guid |
System.Guid |
| char | System.Char |
System.Char |
| array | T[], IList<> |
IList<object> |
| list | IEnumerable<> |
IEnumerable<object> |
| set | ISet<> |
ISet<object> |
| map | IDictionary<,> |
IDictionary<object, object> |
| link | Transit.ILink |
Transit.ILink |
| ratio † | Transit.IRatio |
Transit.IRatio |
| cmap (composite keys) | IDictionary<,> with non-string keys |
Transit.Impl.NullKeyDictionary |
† Extension type
// Write
using var stream = new MemoryStream();
using var writer = TransitFactory.Writer<object>(TransitFactory.Format.Json, stream, ownsStream: false);
writer.Write(myObject);
byte[] bytes = stream.ToArray();
// Read
using var readStream = new MemoryStream(bytes);
var reader = TransitFactory.Reader(TransitFactory.Format.Json, readStream);
var value = reader.Read<object>();// Custom write handler
var customWriteHandlers = new Dictionary<Type, IWriteHandler>
{
[typeof(MyType)] = new MyWriteHandler()
};
using var writer = TransitFactory.Writer<object>(TransitFactory.Format.Json, stream, customWriteHandlers);
// Custom read handler
var customReadHandlers = new Dictionary<string, IReadHandler>
{
["my-tag"] = new MyReadHandler()
};
var reader = TransitFactory.Reader(TransitFactory.Format.Json, stream, customReadHandlers);
// Catch-all Default Write Handler (called for unregistered types)
using var w = TransitFactory.Writer<object>(TransitFactory.Format.Json, stream, null, myDefaultHandler);
// Write-time Transform (modify objects before handler lookup)
Func<object, object> transform = obj => obj is Point p ? new[] { p.X, p.Y } : obj;
using var tw = TransitFactory.Writer<object>(TransitFactory.Format.Json, stream, null, null, transform);If you are creating many readers and writers with custom handlers, you can pre-merge them once to avoid dictionary-merge allocations on every instantiation:
var mergedWriteHandlers = TransitFactory.MergedWriteHandlers(customWriteHandlers);
var mergedReadHandlers = TransitFactory.MergedReadHandlers(customReadHandlers);
// Use the pre-merged frozen maps directly
using var writer = TransitFactory.Writer<object>(TransitFactory.Format.Json, stream, mergedWriteHandlers);
using var reader = TransitFactory.Reader(TransitFactory.Format.Json, stream, mergedReadHandlers);Requires .NET 10 SDK.
# Build
dotnet build src/
# Run tests
dotnet test src/Transit.Tests/
# Run benchmarks (compare old vs new)
dotnet run --project src/Transit.Benchmarks/ -c ReleaseThe Transit.Benchmarks project uses BenchmarkDotNet and references both the original and new implementations side-by-side, measuring throughput and allocations for read, write, and cache-encoding workloads.
src/
Transit/ # Library (this fork)
Impl/ # Internal emitters, parsers, caches, handlers
Numerics/ # BigRational type
Spi/ # Extension SPI (IReaderSpi, etc.)
Transit.Tests/ # Test suite (covers exemplar files from transit-format)
Transit.Benchmarks/ # BenchmarkDotNet comparison harness
src_old/ # Original Beerendonk implementation (reference)
transit-format/ # Upstream Transit specification & exemplar files
transit-java/ # Java reference implementation (submodule)
Copyright © 2014 Rick Beerendonk.
This library is a C# port of the Java version created and maintained by Cognitect, therefore
Copyright © 2014 Cognitect
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.