---
title: "Serialize transcript JSON with NativeAOT in C#"
url: https://recursiveai.org/onespeech/cookbook/serialize-transcript-with-nativeaot/
description: "Save transcript JSON without reflection; no model download is needed."
version: "1.1.0"
---

# Serialize transcript JSON with NativeAOT in C#

**OneSpeech** is a free, open-source speech recognition library for .NET from Recursive AI. MIT or Apache-2.0, no native dependencies.

[NuGet](https://www.nuget.org/packages/RecursiveAI.OneSpeech) · [GitHub](https://github.com/recursive-ai-oss/onespeech-csharp) · [Docs](https://recursiveai.org/onespeech/docs/) · [Download example (.zip)](https://recursiveai.org/onespeech/cookbook/serialize-transcript-with-nativeaot/serialize-transcript-with-nativeaot.zip)

Save transcript JSON without reflection; no model download is needed.

```shell
dotnet add package RecursiveAI.OneSpeech
```

Requires [.NET 10](https://dotnet.microsoft.com/download/dotnet/10.0) (net10.0); .NET 9 and earlier aren’t supported.

```csharp
using OneSpeech;
using System.Text.Json;
using System.Text.Json.Serialization;
var transcript = new Transcript
    { Text = "Hello." };
var typeInfo =
    AppJsonContext.Default.Transcript;
File.WriteAllText("transcript.json",
    JsonSerializer.Serialize(
        transcript, typeInfo));
Console.WriteLine("Saved transcript.json.");
[JsonSerializable(typeof(Transcript))]
internal partial class AppJsonContext
    : JsonSerializerContext;
```

Unzip [the example](https://recursiveai.org/onespeech/cookbook/serialize-transcript-with-nativeaot/serialize-transcript-with-nativeaot.zip), then `dotnet run`.

Prints:

```text
Saved transcript.json.
```

`AppJsonContext` is generated from the partial class in the snippet. The zip’s project disables reflection-based JSON defaults:

```xml
<PropertyGroup>
  <JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>
```

## The full program

```csharp
using OneSpeech;
using System.Text.Json;
using System.Text.Json.Serialization;

var transcript = new Transcript { Text = "Hello." };
var typeInfo = AppJsonContext.Default.Transcript;
string json = JsonSerializer.Serialize(transcript, typeInfo);
File.WriteAllText("transcript.json", json);
var restored = JsonSerializer.Deserialize(json, typeInfo)
    ?? throw new InvalidOperationException("Missing transcript.");
Console.WriteLine("Saved transcript.json.");
Console.WriteLine($"Equal: {transcript == restored}");
Console.WriteLine($"Reflection enabled: {JsonSerializer.IsReflectionEnabledByDefault}");

[JsonSerializable(typeof(Transcript))]
internal partial class AppJsonContext : JsonSerializerContext;
```

The full program reads the JSON back through generated metadata and prints the reflection setting. Invalid documents raise `JsonException` through System.Text.Json.

```text
Saved transcript.json.
Equal: True
Reflection enabled: False
```

The same program was published and run as a Linux x64 native executable with GCC:

```shell
dotnet publish -c Release -r linux-x64 -p:PublishAot=true -p:CppCompilerAndLinker=gcc -o native
./native/Example
```

It printed the same three lines shown above. Use your platform’s NativeAOT toolchain when publishing elsewhere.

## Variations

Register the generated context in serializer options instead:

```csharp
using OneSpeech;
using System.Text.Json;
using System.Text.Json.Serialization;
var options = new JsonSerializerOptions
{
    TypeInfoResolver = AppJsonContext.Default
};
var transcript = new Transcript
    { Text = "Hello." };
string json = JsonSerializer.Serialize(transcript, options);
var restored = JsonSerializer.Deserialize<Transcript>(json, options);
Console.WriteLine(transcript == restored);
[JsonSerializable(typeof(Transcript))]
internal partial class AppJsonContext
    : JsonSerializerContext;
```

```text
True
```

## Related recipes

- [Save a transcript as JSON](https://recursiveai.org/onespeech/cookbook/save-transcript-as-json/)
- [Transcribe an audio file](https://recursiveai.org/onespeech/cookbook/transcribe-audio-file/)

## The short answer

Register `Transcript` with `[JsonSerializable]` on a partial `JsonSerializerContext`, then use the serializer overloads that accept the generated `JsonTypeInfo<Transcript>`. Set `JsonSerializerIsReflectionEnabledByDefault` to `false` in the project to exercise this path even when running normally with `dotnet run`.

This page addresses JSON export and import, not model inference under NativeAOT. The sample constructs a transcript in memory and downloads no models. Its native executable writes the same transcript JSON and restores an equal value with reflection disabled.

## How do I serialize JSON with NativeAOT in C#?

The context class in the snippet is an input to the System.Text.Json source generator. `AppJsonContext.Default.Transcript` is generated during compilation; it is not a property you implement manually. Keep the class partial and include the `JsonSerializable` attribute naming `Transcript`.

Passing the generated type information explicitly makes the required metadata visible at the call site. Do not switch back to a bare `JsonSerializer.Serialize(transcript)` call just because it works in a development build with reflection enabled. Microsoft’s [source-generation guide](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/source-generation) describes the context and type-info overloads and explains how to disable reflection defaults consistently across runtimes.

OneSpeech’s transcript converter remains in control of the wire format. The result is the schema-versioned envelope used by `Transcript.ToJson`, not a second JSON shape generated from public property names. Naming policies cannot turn the transcript into a different schema. Seconds, enum names and explicit nulls follow the transcript contract.

The project in the zip targets .NET 10 and pins the released OneSpeech package. The reflection setting above is part of that project. Native publishing additionally needs the platform’s native toolchain; a successful managed run alone does not prove that your native deployment was built.

## How do I deserialize JSON without reflection in C#?

Use the corresponding `JsonSerializer.Deserialize` overload with the same generated type information. The full program does this immediately after serialization and checks transcript value equality. A null result is rejected by the example rather than being silently treated as an empty transcript.

The converter enforces the same schema and semantic rules as the explicit transcript methods. Missing required fields, invalid timing and unsupported schema versions are errors. Through System.Text.Json, invalid transcript documents raise `JsonException`, rather than the `InvalidTranscriptJsonException` used by `Transcript.FromJson`.

The optional resolver variation registers the generated context through `JsonSerializerOptions.TypeInfoResolver`. This is useful when code passes serializer options through a shared layer rather than accepting a type-info argument directly. Use the explicit type-info overload when you can: it makes the dependency easier to see and avoids accidentally selecting an unconfigured options instance.

## Why does JSON serialization fail when reflection is disabled?

Disabling reflection does not automatically discover your application’s types. If you omit the context, System.Text.Json refuses the operation instead of falling back to a different transcript wire format. Add the generated metadata and use it for both reading and writing; do not re-enable reflection merely to hide missing registration.
