Skip to content

Instantly share code, notes, and snippets.

@davepermen
Last active March 9, 2025 12:33
Show Gist options
  • Save davepermen/5d2ac12ccaa53f9ca1d298e477735eb4 to your computer and use it in GitHub Desktop.
Save davepermen/5d2ac12ccaa53f9ca1d298e477735eb4 to your computer and use it in GitHub Desktop.
using HomeTests;
using JsonTests;
using System.Text.Json;
using WebDeviceTests;
var options = JsonSerializerOptions.Web.WithPolymorphicTypesFor<Event>(configure =>
{
configure
.IncludeTypesFromNamespaceWith<WebButtonEvent>()
.IncludeType<HomeStateEvent>()
;
});
Event[] events = [
new WebButtonEvent("123"),
new WebImageEvent("https://inklay.conesoft.net/cooper.jpg"),
new HomeStateEvent("home/kitchen", "on")
];
var serialized = JsonSerializer.Serialize(events, options);
Console.WriteLine(serialized);
Console.WriteLine();
var _events = JsonSerializer.Deserialize<Event[]>(serialized, options) ?? [];
foreach (var _event in _events)
{
Console.WriteLine(_event switch
{
WebButtonEvent button => $"webbuttonevent: {button.Id}",
HomeStateEvent home => $"homestateevent: {home.Path} = {home.Value}",
WebImageEvent image => $"webimageevent: {image.Url}",
_ => "error"
});
}
/* demo types */
record Event;
namespace WebDeviceTests
{
record WebButtonEvent(string Id) : Event;
record WebImageEvent(string Url) : Event;
}
namespace HomeTests
{
record HomeStateEvent(string Path, string Value) : Event;
}
namespace JsonTests;
public class TypeCollector<Base>(List<Type> types)
{
public TypeCollector<Base> IncludeType<T>() where T : Base
{
types.Add(typeof(T));
return this;
}
public TypeCollector<Base> IncludeTypesFromNamespaceWith<T>()
{
types.AddRange(AppDomain.CurrentDomain
.GetAssemblies()
.SelectMany(a => a.GetTypes())
.Where(t => t.IsSubclassOf(typeof(Base)) && t.Namespace == typeof(T).Namespace)
);
return this;
}
public static IEnumerable<Type> Collect<T>(Action<TypeCollector<T>>? configure)
{
List<Type> types = [];
configure?.Invoke(new TypeCollector<T>(types));
if (configure == null)
{
types.AddRange(AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).Where(t => t.IsSubclassOf(typeof(T))));
}
return types;
}
}
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
namespace JsonTests;
public static class WithPolymorphicTypesExtensions
{
public static JsonSerializerOptions WithPolymorphicTypesFor<T>(this JsonSerializerOptions jsonSerializerOptions, Action<TypeCollector<T>>? configure = null, string? propertyName = null) => new(jsonSerializerOptions)
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver().WithAddedModifier(jsonTypeInfo =>
{
if (jsonTypeInfo.Type == typeof(T))
{
jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions
{
TypeDiscriminatorPropertyName = propertyName ?? "type",
IgnoreUnrecognizedTypeDiscriminators = true,
UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FailSerialization,
};
foreach (var t in TypeCollector<T>.Collect(configure).Select(t => new JsonDerivedType(t, t.Name.ToLowerInvariant())))
{
jsonTypeInfo.PolymorphismOptions.DerivedTypes.Add(t);
}
}
})
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment