Programación y desarrollo de software

Cómo usar unions y jerarquías cerradas en ASP.NET Core

El .NET Blog explica cómo emplear tipos union y jerarquías cerradas con System.Text.Json mediante Minimal APIs, MVC, SignalR, Blazor y OpenAPI. También identifica los casos de ambigüedad y las fuentes de enlace que no admiten estos tipos.

2026-09-10
6 min de lectura
11 visitas
فريق تحرير certi.news
Cómo usar unions y jerarquías cerradas en ASP.NET Core

ASP.NET Core en .NET 11 ofrece compatibilidad práctica para usar tipos union y jerarquías cerradas con System.Text.Json, lo que permite representar un valor que puede pertenecer a más de un tipo o a una familia específica de tipos. Esta compatibilidad se extiende a Minimal APIs, controladores MVC, SignalR, Blazor y la documentación de OpenAPI, con diferencias importantes en la forma de lectura y escritura y en los límites del enlace de parámetros.

¿Cuándo es apropiado un union?

Un union es adecuado para contratos que aceptan alternativas independientes sin necesidad de un envoltorio JSON ni de un campo discriminator. Por ejemplo, un valor puede ser un entero o una cadena, o una configuración puede aceptar un valor booleano o un objeto que contenga opciones más detalladas. Durante la serialización, el caso activo se escribe directamente en su formato natural; no se añade ningún envoltorio especial del tipo union ni ningún discriminator.

Los casos que pueden distinguirse claramente a partir de la estructura JSON se serializan automáticamente. Sin embargo, si los casos comparten la misma estructura, como un union que incluye los objetos Cat y Dog, System.Text.Json no podrá determinar el tipo a partir del token JSON por sí solo. En ese caso, se puede proporcionar un JsonTypeClassifier personalizado para seleccionar el caso correcto. Lo mismo se aplica a algunos contratos de solicitud que aceptan un número o una cadena, ya que las opciones JSON específicas de la web permiten leer números desde cadenas, lo que puede hacer que el texto sea interpretable como cualquiera de los dos casos.

Las jerarquías cerradas son distintas de los union

Usar el modificador closed no significa que la serialización polimórfica se active automáticamente. Al trabajar con un tipo derivado concreto como PaymentAuthorized, System.Text.Json lo trata como cualquier otro tipo y produce JSON que incluye paymentId y amount sin discriminator.

En cambio, al usar el tipo base PaymentEvent en una API, es necesario especificar el tipo derivado que debe crearse. Esto puede activarse en el tipo base mediante JsonPolymorphic(InferClosedTypePolymorphism = true), o a través de JsonSerializerOptions.InferClosedTypePolymorphism. Entonces System.Text.Json infiere los tipos derivados en la serialización cerrada y utiliza sus nombres como identificadores, por ejemplo, $type con el valor PaymentAuthorized.

La elección entre ambos modelos depende de la naturaleza del contrato: un union representa alternativas que quizá no pertenezcan a una misma familia, mientras que las jerarquías cerradas son adecuadas para un conjunto relacionado de eventos o entidades que necesitan un discriminator claro.

Compatibilidad en las capas de ASP.NET Core

En Minimal APIs, un union puede utilizarse como parámetro del cuerpo de la solicitud o como tipo de retorno, tanto en la ruta de ejecución habitual como mediante Request Delegate Generator, con un comportamiento equivalente en ambos caminos. También puede aparecer dentro de otro modelo, como elemento de IAsyncEnumerable<T> o dentro de un contenedor que utilice AsParameters, y las opciones de ConfigureHttpJsonOptions siguen influyendo en la serialización.

Los controladores MVC admiten unions como parámetros de las acciones y como tipos de retorno, incluidos Task<TUnion> y ValueTask<TUnion>. Puesto que MVC utiliza la configuración JsonSerializerDefaults.Web, el problema de ambigüedad entre número y cadena en las entradas HTTP también se aplica a MVC.

En SignalR, la compatibilidad funciona mediante JsonHubProtocol para los parámetros, los valores de retorno y los elementos de los flujos. Un union de tipo int o string no necesita un classifier al utilizar este protocolo, porque JsonHubProtocol no considera ambiguo el token JSON de tipo cadena en este caso. Sin embargo, los unions cuyos casos comparten el token StartObject, como UnionPet(Cat, Dog), siguen necesitando un classifier. SignalR no admite esta función al utilizar los protocolos hub de MessagePack o Newtonsoft.Json.

Blazor y OpenAPI

Los parámetros de los componentes de Blazor dentro del proceso se asignan directamente y, por tanto, no necesitan serialización. Sin embargo, la interoperabilidad con JavaScript, el estado guardado de los componentes y los parámetros durante el prerendering pasan por System.Text.Json, por lo que siguen las mismas reglas de los unions. Por ejemplo, se puede pasar un union a JavaScript con el formato Boolean o como un objeto de opciones, según el caso activo, sin añadir un envoltorio ni un discriminator.

En OpenAPI, un union se representa mediante un esquema anyOf que incluye un tipo para cada caso. Los casos reutilizan los mismos nombres de componentes Cat y Dog cuando aparecen como tipos independientes. Esto difiere de los tipos polimórficos que llevan un discriminator y cuyos esquemas se exponen con nombres distintivos. ApiExplorer también puede representar varios tipos de respuesta para el mismo código de estado y tipo de contenido dentro de anyOf.

Limitaciones que deben tenerse en cuenta

La compatibilidad con unions depende de System.Text.Json y, por ello, no funciona con fuentes de enlace que no pasan por el análisis de JSON. Estas incluyen los valores de query string, los valores de ruta, los encabezados y los campos de formulario. Un valor como ?id=42 no basta para determinar si se pretende un int, un string o un Guid. En Blazor, la limitación se aplica a [SupplyParameterFromQuery] y al enlace desde formularios mediante [SupplyParameterFromForm]. La fuente indica que el enlace de unions desde estas fuentes sigue en fase de estudio y recopilación de comentarios.

Lectura editorial: El valor principal aquí no es añadir un nuevo formato para JSON, sino unificar el tratamiento de contratos con múltiples casos en amplias partes del ecosistema de .NET 11. Sin embargo, el éxito del diseño depende de que los casos puedan distinguirse y de la fuente de datos; por ello, conviene definir pronto un classifier o discriminator y no asumir que la compatibilidad con la serialización implica compatibilidad con todos los métodos de enlace de parámetros.

Fuente de la noticia
ف
Autor

فريق تحرير certi.news

De la misma categoría

También te puede interesar

Ver todas las noticias