Model variable JSON field types with unions

September 25th 2026 C# 15 C# .NET

A while back I wrote a blog post about deserializing a JSON with a field of a variable type in .NET. At that time I used a custom serializer to achieve that. This scenario seems like a good fit for unions which are being added in C# 15. That's a good reason to revisit the topic.

I was dealing with a field that could be serialized into two different JSON types, depending on its contents. It could be serialized as string:

{
  "Version": "1.0.0"
}

Or, it could be serialized as a number:

{
  "Version": 1
}

A union type is dedicated to modeling data which can be represented as one of multiple types, in this case a string or a number. And if you need to always access it as a string as I did, you can add custom property for that:

public union StringOrNumber(string, long)
{
    public string? StringValue => this switch
    {
        string stringValue => stringValue,
        long longValue => longValue.ToString(),
        null => null
    };
}

You can now use this type to model the multi-type field in the JSON object:

public class AppInfo
{
    public StringOrNumber Version { get; init; }
}

With this setup, the deserialized value is typed correctly for both of the above cases, i.e., string or number, respectively. And the StringValue returns its string equivalent:

[Test]
public void DeserializesStringValue()
{
    const string json = """{"Version":"1.0.0"}""";

    var appInfo = JsonSerializer.Deserialize<AppInfo>(json);

    Assert.That(appInfo, Is.Not.Null);
    Assert.That(appInfo.Version.Value, Is.EqualTo("1.0.0"));
    Assert.That(appInfo.Version.StringValue, Is.EqualTo("1.0.0"));
}

[Test]
public void DeserializesNumericValue()
{
    const string json = """{"Version":1}""";

    var appInfo = JsonSerializer.Deserialize<AppInfo>(json);

    Assert.That(appInfo, Is.Not.Null);
    Assert.That(appInfo.Version.Value, Is.EqualTo(1));
    Assert.That(appInfo.Version.StringValue, Is.EqualTo("1"));
}

And if the Version field is missing or set to null, the typed and string value are also both null. Only the union type is non-null since it's a value type:

[Test]
public void DeserializesMissingValue()
{
    const string json = """{"Version":null}""";

    var appInfo = JsonSerializer.Deserialize<AppInfo>(json);

    Assert.That(appInfo, Is.Not.Null);
    Assert.That(appInfo.Version.Value, Is.EqualTo(null));
    Assert.That(appInfo.Version.StringValue, Is.EqualTo(null));
}

You can find a sample project with all code from this blog post in my GitHub repository. It includes the tests for serialization as well. It works as expected without any additional code.

Union types are a useful addition to C#. Some scenarios which required special handling in the past now just work out of the box. To try them out, you will need the latest .NET 11 SDK pre-release, Release Candidate 1 at the time of writing. It comes even with a go-live support license, so you can deploy it to production if you feel courageous. I would suggest you wait at least until the final release in November, though.

Get notified when a new blog post is published (usually every Friday):

Copyright
Creative Commons License