TextEncoding
Decode bytes written in Shift_JIS, windows-1256, ISO-8859-7 or any other legacy character set using the browser's own decoder - the one part of the Encoding API that .NET on WebAssembly does not bring with it.
@inject Bit.Butil.TextEncoding textEncodingMDN reference
System.Text.Encoding.CodePages package plus a registration call, and its data is
dead weight in a published WebAssembly app. The browser already implements every label in the
encoding standard, so borrowing its decoder is usually the shorter path. Encoding is the other
way round: TextEncoder only ever produces UTF-8, which .NET does perfectly well on
its own.
IsSupported says the runtime has TextDecoder at all. IsEncodingSupported asks about one label - there is no list of encodings anywhere in the platform, so it is answered by constructing a decoder and seeing whether it throws. GetCanonicalName normalizes an alias: 'sjis' and 'shift-jis' both come back as 'shift_jis'.
@inject Bit.Butil.TextEncoding textEncoding
var supported = await textEncoding.IsSupported();
var canDecode = await textEncoding.IsEncodingSupported("shift_jis");
var name = await textEncoding.GetCanonicalName("sjis"); // "shift_jis"The whole point of the class. Each sample below is the same greeting written in a different code page - bytes that are meaningless as UTF-8. Decode them with the right label and they read correctly; decode them as UTF-8 and you get the mojibake every legacy import starts with.
var text = await textEncoding.Decode(bytes, "shift_jis");
// Guessing at the encoding? Turn on fatal and a wrong guess
// comes back as null instead of as replacement characters:
var strict = await textEncoding.Decode(bytes, "shift_jis", fatal: true);TextEncoder produces UTF-8 and nothing else - there is no legacy encoder in the platform, by design. GetUtf8ByteLength answers the length question without moving the bytes across the interop boundary, which is what a check against a server-side limit actually needs.
var bytes = await textEncoding.Encode("héllo"); // byte[]
var length = await textEncoding.GetUtf8ByteLength("héllo"); // 6, not 5Content that arrives a chunk at a time - a download, a fetch body, a file read piece by piece - can split a multi-byte character across two chunks. Decoding each chunk on its own turns that character into replacement characters; a streaming decoder holds the incomplete sequence until the next chunk completes it. The demo splits the Shift_JIS sample straight through the middle of a character to show the difference.
await using var decoder = await textEncoding.CreateDecoder("shift_jis");
foreach (var chunk in chunks)
{
text += await decoder.Decode(chunk); // more: true is the default
}
text += await decoder.Flush(); // emits anything still pendingAPI reference
ValueTask<bool> IsSupported()ValueTask<bool> IsEncodingSupported(string label)ValueTask<string?> GetCanonicalName(string label)ValueTask<string?> Decode(byte[] bytes, string label = "utf-8", bool fatal = false, bool ignoreBom = false)ValueTask<byte[]> Encode(string text)ValueTask<int> GetUtf8ByteLength(string text)ValueTask<TextDecoderHandle?> CreateDecoder(string label = "utf-8", bool fatal = false, bool ignoreBom = false)ValueTask DisposeAsync()ValueTask<string?> Decode(byte[] bytes, bool more = true)ValueTask<string?> Flush()ValueTask DisposeAsync()