Search code examples
c#.netsystem.text.jsonnative-aot

How can I preserve specific built-in types for System.Text.Json AOT?


I have a object structure like List<Dictionary<string, int>> and I serialize it directly with JsonSerializer. Obviously I can't use it as it is for AOT or I get:

JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.

Now I know how it is typically done when using your own custom types, but what are my options for built-in?


Solution

  • You set up JSON source generation for system types TSystem in exactly the same way you do for custom types: define some JsonSerializerContext with [JsonSerializable(typeof(TSystem))] applied:

    [JsonSourceGenerationOptions(WriteIndented = true)] // Add whatever options you want here.
    [JsonSerializable(typeof(List<Dictionary<string, int>>))]
    // Add additional [JsonSerializable(typeof(T))] attributes as required.
    public partial class MySerializationContext : JsonSerializerContext;
    

    The only question is, what JsonTypeInfo<TSystem> property name will be generated for MySerializationContext for a generic system type like List<Dictionary<string, int>>? As it turns out, it's formed by concatenating all the type names in order, i.e. ListDictionaryStringInt32:

    List<Dictionary<string, int>> list = new()
    {
        new() { {"foo", 10}, {"bar", 20}, }
    };
    
    var json = JsonSerializer.Serialize(
        list, 
        MySerializationContext.Default.ListDictionaryStringInt32);
    

    If you're not sure, you can instead call JsonSerializerContext.GetTypeInfo(Type) to get the required type info in runtime:

    var typeInfo = (JsonTypeInfo<List<Dictionary<string, int>>>?)MySerializationContext.Default.GetTypeInfo(list.GetType());
    if (typeInfo == null)
        throw new JsonException($"TypeInfo not found for {list.GetType()}");
    var json = JsonSerializer.Serialize(list, typeInfo);
    

    You can also determine the property name by using reflection in the debugger:

    Console.WriteLine(string.Join(",", MySerializationContext.Default.GetType().GetProperties().Select(p => p.Name)));