System.Text.Json учится сериализовать union-типы C# в .NET 11 Preview 6
Как System.Text.Json в .NET 11 Preview 6 сериализует новые union-типы C#, записывая активный случай, и API JsonUnionAttribute и классификаторов типов, которые разрешают неоднозначные случаи.
Union-типы C# были главной возможностью предварительных версий .NET 11, но до сих пор они останавливались на границе компилятора. Начиная с .NET 11 Preview 6 (9 июля 2026 года), System.Text.Json понимает их нативно: вы можете сериализовать и десериализовать union, не написав собственный JsonConverter. В Preview 6 встали на место ещё две детали, поэтому всё это наконец работает без шаблонного кода: вспомогательные типы System.Runtime.CompilerServices.UnionAttribute и IUnion теперь входят в состав фреймворка, так что union компилируется в обычном проекте net11.0.
Записывается активный случай, а не обёртка
Union объявляет, что значение является ровно одним из фиксированного набора типов-случаев. Сокращённая форма генерирует структуру, которая содержит тот случай, что активен в данный момент:
public union Pet(Cat, Dog, Bird);
public record Cat(string Name);
public record Dog(string Name);
public record Bird(string Name);
System.Text.Json распознаёт это через новый вид контракта, JsonTypeInfoKind.Union. Когда вы сериализуете union, сериализатор читает активный случай и записывает его значение напрямую, без какой-либо обёртки вокруг:
Pet pet = new Dog("Rex");
string json = JsonSerializer.Serialize(pet);
// {"Name":"Rex"}
Для union из примитивов это означает, что union из int и string проходит цикл туда и обратно без потерь, потому что токены JSON структурно различны:
Pet-like union of (int, string):
"hello" // the string case
42 // the int case
Это поддерживают и сериализатор на основе рефлексии, и генератор исходного кода, так что вам не приходится сходить с дружественного к AOT пути, чтобы этим воспользоваться.
Пробел с дискриминатором и как его закрыть
Записывать сырое значение элегантно, но обратите внимание, что происходит с Pet: и Dog("Rex"), и Cat("Rex") сериализуются в {"Name":"Rex"}. На обратном пути сериализатор не может определить, какой это был случай. Это классическая проблема помеченных объединений, и Preview 6 даёт вам инструменты, чтобы решить её, а не гадать.
Три новых API управляют тем, как случаи обнаруживаются и именуются: JsonUnionAttribute, JsonUnionCaseInfo и пара классификаторов типов JsonTypeClassifier и JsonSerializerOptions.TypeClassifiers. Вместе они позволяют прикрепить дискриминатор типа к формируемому JSON, чтобы неоднозначные случаи в форме объекта десериализовались обратно в правильный тип-случай. Структурно различным случаям (как int против string) ничего из этого не нужно; вся церемония вступает в игру только тогда, когда полезные нагрузки иначе бы столкнулись.
Если вы следили за освещением union-типов начиная с Preview 2, это та деталь, которая делает их пригодными к использованию через границу HTTP. Union всегда должны были жить или умереть в зависимости от поддержки сериализации, и в Preview 6 они перестают быть курьёзом компилятора и становятся тем, что можно отправить по сети.
Все подробности приведены в примечаниях к выпуску библиотек Preview 6.
Comments
Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.