.NET Codec Extension Public Interface¶
.NET per-language interface table of contents · Common Message Contract · Common Framework API
1. Scope¶
This document only fixes the C# API an application uses to register a codec extension, and the SPI an external codec provider implements. The default JSON behavior, packet name decision, the JSON format of a global object reference, and payload ownership are owned by the common message contract. The internal registry, codec selection cache, and dispatch implementation aren't this document's contract.
An application using only JSON doesn't call the codec API. An
application using an optional codec package registers one extension
instance with IZLinkCodecRegistryBuilder.Use(...).
IZLinkCodecRegistrar, IZLinkMessageSerializer, and
ZLinkEncodedPayload are the SPI only a custom codec provider
implements.
2. Codec Registration API And Provider SPI¶
IZLinkCodecRegistrar only registers a business payload serializer. The
STREAM header's codec value is declared with the Stream Connector-owned
IZlinkStreamCodecRegistration. This separation keeps the HTTP client
from depending on the STREAM runtime or the compression package.
contentType takes a parameter-free ASCII type/subtype. At startup, the registry removes
leading and trailing SP and TAB and converts ASCII uppercase letters to lowercase. The
result is the canonical content type used as the registry key. A parameter, whitespace
inside the value, or a non-ASCII token causes ArgumentException. If the same canonical
content type is registered again, the last serializer replaces the earlier one.
A value received from the framework service wire must already be canonical. Another
representation completes with ZLinkFrameworkErrorKind.ProtocolError. The HTTP client
first parses response parameters and passes only the parameter-free media type to this
rule.
Send-side serializer selection uses the message type declared at the call site, not the
concrete type of the instance. The three-argument overload passes this declared type to
canSerialize. If more than one registration returns true, the serializer registered
later is used. If no registration matches, the JSON serializer is used. When multiple
fallback serializers are registered with the two-argument overload, the later registration
also replaces the earlier one.
The registry doesn't change after startup. Send-selection results are stored for up to 1,024 declared types. Reaching the limit doesn't evict existing entries. Each type first seen afterward is re-evaluated against the registration list on every send, and its result isn't stored.
The receive path performs an serializer lookup using the canonical content type from
the wire. An unregistered or noncanonical value isn't reinterpreted as JSON and completes
with ZLinkFrameworkErrorKind.ProtocolError.
public interface IZLinkCodecExtension
{
void Register(IZLinkCodecRegistrar codecs);
}
public interface IZLinkCodecRegistryBuilder
{
void Use(IZLinkCodecExtension extension);
}
public interface IZLinkCodecRegistrar
{
void AddSerializer(
string contentType,
IZLinkMessageSerializer serializer);
void AddSerializer(
string contentType,
IZLinkMessageSerializer serializer,
Func<Type, bool> canSerialize);
}
public interface IZLinkMessageSerializer
{
ZLinkEncodedPayload Serialize(object value, Type type);
object? Deserialize(ZLinkEncodedPayload payload, Type type);
}
public readonly struct ZLinkEncodedPayload : IEquatable<ZLinkEncodedPayload>
{
public ReadOnlyMemory<byte> Bytes { get; }
public static ZLinkEncodedPayload From(byte[] bytes);
public static ZLinkEncodedPayload From(ReadOnlyMemory<byte> bytes);
public static ZLinkEncodedPayload From(ReadOnlySpan<byte> bytes);
public byte[] ToArray();
public bool Equals(ZLinkEncodedPayload other);
public override bool Equals(object? obj);
public override int GetHashCode();
public static bool operator ==(
ZLinkEncodedPayload left,
ZLinkEncodedPayload right);
public static bool operator !=(
ZLinkEncodedPayload left,
ZLinkEncodedPayload right);
}
The official codec package's Default is a shared instance with no
separate state. The two classes together implement the Framework
server's codec extension and the Stream Connector's typed payload codec.
public sealed class ZLinkMessagePackCodec :
IZLinkCodecExtension,
IZlinkStreamPayloadCodec,
IZlinkStreamCodecRegistration
{
public static ZLinkMessagePackCodec Default { get; }
public void Register(IZLinkCodecRegistrar codecs);
public ZlinkStreamEncodedPayload Encode<TPayload>(TPayload payload);
public TPayload Decode<TPayload>(ZlinkStreamEncodedPayload payload);
}
public sealed class ZLinkProtobufCodec :
IZLinkCodecExtension,
IZlinkStreamPayloadCodec,
IZlinkStreamCodecRegistration
{
public static ZLinkProtobufCodec Default { get; }
public void Register(IZLinkCodecRegistrar codecs);
public ZlinkStreamEncodedPayload Encode<TPayload>(TPayload payload);
public TPayload Decode<TPayload>(ZlinkStreamEncodedPayload payload);
}