<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/the-pain-of-serializing-unions-and-closed-class-hierarchies-with-system-text-json-5gdr03rjh" -->

---
title: The pain of serializing unions and closed class...
description: An in-depth exploration of how System.Text.Json handles the new C# 15 union types and closed class hierarchies coming in .NET 11 preview 7. Serializing a...
canonical: https://daily.dev/posts/the-pain-of-serializing-unions-and-closed-class-hierarchies-with-system-text-json-5gdr03rjh
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: The pain of serializing unions and closed class hierarchies with System.Text.Json | daily.dev
og:description: An in-depth exploration of how System.Text.Json handles the new C# 15 union types and closed class hierarchies coming in .NET 11 preview 7. Serializing a...
og:url: https://daily.dev/posts/the-pain-of-serializing-unions-and-closed-class-hierarchies-with-system-text-json-5gdr03rjh
og:image: https://api.daily.dev/og/posts/5gdR03rjH.png
og:image:alt: The pain of serializing unions and closed class hierarchies with System.Text.Json
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# The pain of serializing unions and closed class hierarchies with System.Text.Json

**[.NET Escapades](https://daily.dev/sources/andrewlock)** · 17 min read · 10 upvotes · 0 comments

## Summary

An in-depth exploration of how System.Text.Json handles the new C# 15 union types and closed class hierarchies coming in .NET 11 preview 7. Serializing a closed hierarchy like Pet/Dog/Labrador loses derived-type information by default, and the new InferClosedTypePolymorphism option fails with multiple exceptions when hierarchies have more than one level or mixed accessibility, forcing manual [JsonDerivedType] annotations instead. Deserializing unions such as SupportedOS(Windows, Linux, MacOS) throws an ambiguity exception because all three cases serialize as JSON objects, requiring a custom JsonTypeClassifierFactory; a general JsonUnionTypeStructuralClassifier is coming in RC1 but has its own limitations, including poor performance and incompatibility with nested unions or non-unique properties.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://andrewlock.net/exploring-the-dotnet-11-preview-7-the-pain-of-serializing-unions-and-closed-class-hierarchies-with-system-text-json>

## Questions this post answers

### Why does System.Text.Json throw an ambiguous JSON value type exception when deserializing a union like SupportedOS(Windows, Linux, MacOS)?

It happens because all three case types (Windows, Linux, MacOS) serialize as a JSON Object, and System.Text.Json's default union deserialization only works automatically when each case serializes to a distinct JSON primitive type such as Number, String, or Bool. Since multiple cases share the Object shape, the deserializer cannot determine which type to instantiate and throws a JsonException requiring a custom type classifier.

_Anyone wiring up union deserialization in .NET 11 can track these edge cases via daily.dev before hitting the same exception._

### Why does InferClosedTypePolymorphism fail when serializing a two-level closed class hierarchy in .NET 11 with System.Text.Json?

InferClosedTypePolymorphism throws exceptions such as 'Runtime type Labrador is not supported by polymorphic type Pet' because it only reliably infers direct descendants of a closed type, not deeper nested hierarchies like Pet to Dog to Labrador, and it also requires derived types to be at least as accessible as the base type. Workarounds are limited to a single derived type per closed class or manually specifying derived types with [JsonDerivedType].

_Teams evaluating .NET 11 preview features for production use can follow this kind of gotcha on daily.dev before upgrading._

### How do I fix union deserialization in System.Text.Json when multiple union cases are all JSON objects with no discriminator?

Implement a custom JsonTypeClassifierFactory that reads the raw JSON with a Utf8JsonReader, inspects property names to identify a unique field per case (for example Distro for Linux or Name for MacOS), and returns the matching type; apply it to the union with the [JsonUnion(TypeClassifier = ...)] attribute. A generalized JsonUnionTypeStructuralClassifier addressing this is planned for RC1 but has its own limitations, including poor performance and failure on nested unions.

_Developers weighing structural classifiers versus type discriminators can compare approaches like this on daily.dev._

---

Tags: [#.net](https://daily.dev/tags/.net), [#c#](https://daily.dev/tags/c#)

[View this post on daily.dev](https://daily.dev/posts/the-pain-of-serializing-unions-and-closed-class-hierarchies-with-system-text-json-5gdr03rjh)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"The pain of serializing unions and closed class hierarchies with System.Text.Json","url":"https://daily.dev/posts/the-pain-of-serializing-unions-and-closed-class-hierarchies-with-system-text-json-5gdr03rjh","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/the-pain-of-serializing-unions-and-closed-class-hierarchies-with-system-text-json-5gdr03rjh"},"datePublished":"2026-09-01T10:17:28.912Z","dateModified":"2026-09-01T10:17:55.295Z","description":"An in-depth exploration of how System.Text.Json handles the new C# 15 union types and closed class hierarchies coming in .NET 11 preview 7. Serializing a...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/d8309250ceef9395e962424a615ecc3c?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/d8309250ceef9395e962424a615ecc3c?_a=AQAEuop","isAccessibleForFree":true,"articleSection":".NET Escapades","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":".NET Escapades","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/74ebd66cf7e840b6b29913eae3da9189","url":"https://daily.dev/sources/andrewlock"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/the-pain-of-serializing-unions-and-closed-class-hierarchies-with-system-text-json-5gdr03rjh","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":10},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":".net,c#","timeRequired":"PT17M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":".NET Escapades","item":"https://daily.dev/sources/andrewlock"},{"@type":"ListItem","position":3,"name":"The pain of serializing unions and closed class hierarchies with System.Text.Json"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/the-pain-of-serializing-unions-and-closed-class-hierarchies-with-system-text-json-5gdr03rjh#faq","mainEntity":[{"@type":"Question","name":"Why does System.Text.Json throw an ambiguous JSON value type exception when deserializing a union like SupportedOS(Windows, Linux, MacOS)?","acceptedAnswer":{"@type":"Answer","text":"It happens because all three case types (Windows, Linux, MacOS) serialize as a JSON Object, and System.Text.Json's default union deserialization only works automatically when each case serializes to a distinct JSON primitive type such as Number, String, or Bool. Since multiple cases share the Object shape, the deserializer cannot determine which type to instantiate and throws a JsonException requiring a custom type classifier. Anyone wiring up union deserialization in .NET 11 can track these edge cases via daily.dev before hitting the same exception."}},{"@type":"Question","name":"Why does InferClosedTypePolymorphism fail when serializing a two-level closed class hierarchy in .NET 11 with System.Text.Json?","acceptedAnswer":{"@type":"Answer","text":"InferClosedTypePolymorphism throws exceptions such as 'Runtime type Labrador is not supported by polymorphic type Pet' because it only reliably infers direct descendants of a closed type, not deeper nested hierarchies like Pet to Dog to Labrador, and it also requires derived types to be at least as accessible as the base type. Workarounds are limited to a single derived type per closed class or manually specifying derived types with [JsonDerivedType]. Teams evaluating .NET 11 preview features for production use can follow this kind of gotcha on daily.dev before upgrading."}},{"@type":"Question","name":"How do I fix union deserialization in System.Text.Json when multiple union cases are all JSON objects with no discriminator?","acceptedAnswer":{"@type":"Answer","text":"Implement a custom JsonTypeClassifierFactory that reads the raw JSON with a Utf8JsonReader, inspects property names to identify a unique field per case (for example Distro for Linux or Name for MacOS), and returns the matching type; apply it to the union with the [JsonUnion(TypeClassifier = ...)] attribute. A generalized JsonUnionTypeStructuralClassifier addressing this is planned for RC1 but has its own limitations, including poor performance and failure on nested unions. Developers weighing structural classifiers versus type discriminators can compare approaches like this on daily.dev."}}]}
```

