[API Proposal]: Handle union as a xsd:choice in XmlSerializer as a type safe exhaustive alternative
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
### Background and motivation
*I apologize if I used wrong issue template but I believe the only other is a blank one which isn't any better for this proposal.*
With C# 15 and .NET 11 bringing union types I think now's a good time to propose a type-safe xsd:choice and XmlSerializer.
Currently, for this schema:
```xsd
```
and an instance document that has to pick exactly one of the two:
```xml
4
```
To consume that Drawing class today, XmlSerializer needs Outline typed as `object` with stacked XmlElementAttributes:
```cs
public class Drawing
{
[XmlElement("Circle", typeof(Circle))]
[XmlElement("Rectangle", typeof(Rectangle))]
public object Outline { get; set; }
}
```
That schema is a closed, type-safe constraint — Outline is either Circle or Rectangle and nothing else. But in C# it can't be said the same. `object` accepts anything. The choice between Circle and Rectangle is not enforced and it's up to consumers of this class to make sure they provided the right types.
The situation is even worse for repeated choice:
```xsd
```
```xml
4
23
1
```
For XmlSerializer to be able to deserialize Xml files described with such schema a new property for Drawing needs to be introduced:
```cs
public class Drawing
{
[XmlElement("Circle", typeof(Circle))]
[XmlElement("Rectangle", typeof(Rectangle))]
[XmlChoiceIdentifier(nameof(ShapesElementName))]
public object[] Shapes;
[XmlIgnore]
public ShapeChoice[] ShapesElementName;
}
public enum ShapeChoice { Circle, Rectangle }
```
This isn't type safe nor intuitive. And that's not all. Not only that another property `ShapesElementName` is there *only* for XmlSerializer but also a *new mandatory* enum with value names that **have to match** element names has to be defined.
### API Proposal
So my proposal is simple:
No surface API change whatsoever. The only change would be in XmlSerializer's deserialization/serialization behavior. A type safe, intuitive and exhaustive alternative.
Instead of `object` or `object[]` XmlSerializer would use unions to enforce type safety. See below in API Usage.
To be clear I do not propose a complete overhaul change of xsd:choice. Keep the old XmlChoiceIdentifier and add new functionality to the existing API.
### API Usage
So with unions the structure would look like this:
```cs
public union Outline(Circle, Rectangle);
public class Drawing
{
[XmlElement]
public Outline Outline;
}
```
or with an array:
```cs
public union Shape(Circle, Rectangle);
public class Drawing
{
[XmlArray]
public Shape[] Shapes;
}
```
Serializing and deserializing would not need to change on the API level:
```cs
XmlSerializer serializer = new(typeof(Drawing));
Drawing? drawing = (Drawing?)serializer.Deserialize(File.OpenRead("drawings.xml"));
```
### Alternative Designs
Maybe a new attribute that would decorate a union which would tell XmlSerializer that it's a choice might be needed. I do not see that deep into .NET runtime:
```cs
[XmlChoice]
public union Outline(Circle, Rectangle);
```
### Risks
Currently none since unions are in preview.
But if not implemented in the upcoming .NET 11 and rather in later versions some breaking changes might arise.
Hopefully this will get resolve before that but by looking on the amount of open issues I can only have hopes.
Contributor guide
Research direction
The proposal targets XmlSerializer serialization and deserialization of xsd:choice, using the Drawing, Outline, and Shape examples and the existing XmlChoiceIdentifier API. Start by reviewing how XmlSerializer currently handles these choice members. Done would require a feasible design for union-based handling without a surface API change, along with defined serialization and deserialization behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- api, backend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100